From 26dfb0de5b12cb269c171843a674c7d14232dcbd Mon Sep 17 00:00:00 2001 From: Ron Cohen Date: Fri, 28 Mar 2025 15:26:01 +0100 Subject: [PATCH 1/2] Revert "feat: use webcrypto API to support edge runtimes (#384)" This reverts commit 6302392435ec3ea1e479a461014543d4b3e23060. --- packages/flag-evaluation/src/index.ts | 50 +++++++++------------ packages/flag-evaluation/test/index.test.ts | 16 +++---- yarn.lock | 9 +--- 3 files changed, 29 insertions(+), 46 deletions(-) diff --git a/packages/flag-evaluation/src/index.ts b/packages/flag-evaluation/src/index.ts index d3bbd5ff..293d43f6 100644 --- a/packages/flag-evaluation/src/index.ts +++ b/packages/flag-evaluation/src/index.ts @@ -259,7 +259,7 @@ export function unflattenJSON(data: Record): Record { * @param {string} hashInput - The input string used to generate the hash. * @return {number} A number between 0 and 100000 derived from the hash of the input string. */ -export async function hashInt(hashInput: string): Promise { +export function hashInt(hashInput: string): number { // 1. hash the key and the partial rollout attribute // 2. take 20 bits from the hash and divide by 2^20 - 1 to get a number between 0 and 1 // 3. multiply by 100000 to get a number between 0 and 100000 and compare it to the threshold @@ -344,11 +344,11 @@ export function evaluate( } } -async function evaluateRecursively( +function evaluateRecursively( filter: RuleFilter, context: Record, missingContextFieldsSet: Set, -): Promise { +): boolean { switch (filter.type) { case "constant": return filter.value; @@ -369,38 +369,30 @@ async function evaluateRecursively( return false; } - const hashVal = await hashInt( + const hashVal = hashInt( `${filter.key}.${context[filter.partialRolloutAttribute]}`, ); return hashVal < filter.partialRolloutThreshold; } - case "group": { - const isAnd = filter.operator === "and"; - let result = isAnd; - for (const current of filter.filters) { - // short-circuit if we know the result already - // could be simplified to isAnd !== result, but this is more readable - if ((isAnd && !result) || (!isAnd && result)) { - return result; + case "group": + return filter.filters.reduce((acc, current) => { + if (filter.operator === "and") { + return ( + acc && + evaluateRecursively(current, context, missingContextFieldsSet) + ); } - - const newRes = await evaluateRecursively( - current, - context, - missingContextFieldsSet, + return ( + acc || evaluateRecursively(current, context, missingContextFieldsSet) ); - - result = isAnd ? result && newRes : result || newRes; - } - return result; - } + }, filter.operator === "and"); case "negation": - return !(await evaluateRecursively( + return !evaluateRecursively( filter.filter, context, missingContextFieldsSet, - )); + ); default: return false; } @@ -442,18 +434,16 @@ export interface EvaluationResult { missingContextFields?: string[]; } -export async function evaluateFeatureRules({ +export function evaluateFeatureRules({ context, featureKey, rules, -}: EvaluationParams): Promise> { +}: EvaluationParams): EvaluationResult { const flatContext = flattenJSON(context); const missingContextFieldsSet = new Set(); - const ruleEvaluationResults = await Promise.all( - rules.map((rule) => - evaluateRecursively(rule.filter, flatContext, missingContextFieldsSet), - ), + const ruleEvaluationResults = rules.map((rule) => + evaluateRecursively(rule.filter, flatContext, missingContextFieldsSet), ); const missingContextFields = Array.from(missingContextFieldsSet); diff --git a/packages/flag-evaluation/test/index.test.ts b/packages/flag-evaluation/test/index.test.ts index 665575bc..7739f009 100644 --- a/packages/flag-evaluation/test/index.test.ts +++ b/packages/flag-evaluation/test/index.test.ts @@ -38,7 +38,7 @@ const feature = { describe("evaluate feature targeting integration ", () => { it("evaluates all kinds of filters", async () => { - const res = await evaluateFeatureRules({ + const res = evaluateFeatureRules({ featureKey: "feature", rules: [ { @@ -109,7 +109,7 @@ describe("evaluate feature targeting integration ", () => { }); it("evaluates flag when there's no matching rule", async () => { - const res = await evaluateFeatureRules({ + const res = evaluateFeatureRules({ ...feature, context: { company: { @@ -137,7 +137,7 @@ describe("evaluate feature targeting integration ", () => { }, }; - const res = await evaluateFeatureRules({ + const res = evaluateFeatureRules({ ...feature, context, }); @@ -155,7 +155,7 @@ describe("evaluate feature targeting integration ", () => { }); it("evaluates flag with missing values", async () => { - const res = await evaluateFeatureRules({ + const res = evaluateFeatureRules({ featureKey: "feature", rules: [ { @@ -198,7 +198,7 @@ describe("evaluate feature targeting integration ", () => { }); it("returns list of missing context keys ", async () => { - const res = await evaluateFeatureRules({ + const res = evaluateFeatureRules({ ...feature, context: {}, }); @@ -214,7 +214,7 @@ describe("evaluate feature targeting integration ", () => { }); it("fails evaluation and includes key in missing keys when rollout attribute is missing from context", async () => { - const res = await evaluateFeatureRules({ + const res = evaluateFeatureRules({ featureKey: "myfeature", rules: [ { @@ -352,8 +352,8 @@ describe("rollout hash", () => { ] as const; for (const [input, expected] of tests) { - it(`evaluates '${input}' = ${expected}`, async () => { - const res = await hashInt(input); + it(`evaluates '${input}' = ${expected}`, () => { + const res = hashInt(input); expect(res).toEqual(expected); }); } diff --git a/yarn.lock b/yarn.lock index 3fca25b4..f221b850 100644 --- a/yarn.lock +++ b/yarn.lock @@ -545,14 +545,7 @@ __metadata: languageName: unknown linkType: soft -"@bucketco/flag-evaluation@npm:~0.1.0": - version: 0.1.0 - resolution: "@bucketco/flag-evaluation@npm:0.1.0" - checksum: 10c0/a5d747962d43ce12b194735e92524a576edac9e9ad53c425b4a517123ca9918d3001891cd212f178b7cf6b235c79aa5cfa3942e162187da056c2ae1d5230a984 - languageName: node - linkType: hard - -"@bucketco/flag-evaluation@workspace:packages/flag-evaluation": +"@bucketco/flag-evaluation@npm:~0.1.0, @bucketco/flag-evaluation@workspace:packages/flag-evaluation": version: 0.0.0-use.local resolution: "@bucketco/flag-evaluation@workspace:packages/flag-evaluation" dependencies: From 7ee93f7f28308207f9e906a7d999aa790e164bb1 Mon Sep 17 00:00:00 2001 From: Ron Cohen Date: Fri, 28 Mar 2025 15:26:39 +0100 Subject: [PATCH 2/2] bumb version again --- packages/flag-evaluation/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/flag-evaluation/package.json b/packages/flag-evaluation/package.json index 8df1442b..b3add0f5 100644 --- a/packages/flag-evaluation/package.json +++ b/packages/flag-evaluation/package.json @@ -1,6 +1,6 @@ { "name": "@bucketco/flag-evaluation", - "version": "0.1.1", + "version": "0.1.2", "license": "MIT", "repository": { "type": "git",