Skip to content

Commit 1ebce2c

Browse files
committed
Cleanup
1 parent 93a5316 commit 1ebce2c

File tree

2 files changed

+63
-107
lines changed

2 files changed

+63
-107
lines changed

packages/vercel-flags-core/src/evaluate.test.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1368,7 +1368,7 @@ describe('evaluate', () => {
13681368
result: false,
13691369
},
13701370

1371-
// ---- Case-insensitive (ci option) ----
1371+
// ---- Case-insensitive (4th element: { ci: true }) ----
13721372

13731373
// EQ { ci: true }
13741374
{
@@ -1395,6 +1395,12 @@ describe('evaluate', () => {
13951395
entities: {},
13961396
result: false,
13971397
},
1398+
{
1399+
name: `${Comparator.EQ} ci with number (no effect)`,
1400+
condition: [['user', 'age'], Comparator.EQ, 42, { ci: true }],
1401+
entities: { user: { age: 42 } },
1402+
result: true,
1403+
},
13981404

13991405
// NOT_EQ { ci: true }
14001406
{

packages/vercel-flags-core/src/evaluate.ts

Lines changed: 56 additions & 106 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,10 @@ import {
88
ResolutionReason,
99
} from './types';
1010

11-
type PathArray = (string | number)[];
12-
1311
const MAX_REGEX_INPUT_LENGTH = 10_000;
1412

13+
type PathArray = (string | number)[];
14+
1515
function exhaustivenessCheck(_: never): never {
1616
throw new Error('Exhaustiveness check failed');
1717
}
@@ -56,7 +56,9 @@ function isArray(input: unknown): input is unknown[] {
5656
}
5757

5858
function lower(input: unknown): unknown {
59-
return typeof input === 'string' ? input.toLowerCase() : input;
59+
if (typeof input === 'string') return input.toLowerCase();
60+
if (Array.isArray(input)) return input.map(lower);
61+
return input;
6062
}
6163

6264
function matchTargetList<T>(
@@ -131,64 +133,45 @@ function matchConditions<T>(
131133
params: EvaluationParams<T>,
132134
): boolean {
133135
return conditions.every((condition) => {
134-
const [lhsAccessor, cmpKey, rhs, options] = condition;
135-
const ci = options && 'ci' in options && options.ci === true;
136+
const [lhsAccessor, cmpKey, rawRhs, options] = condition;
137+
const ci = options !== undefined && options.ci === true;
136138

139+
// ci is not applicable to segment conditions (segments are internal IDs)
137140
if (lhsAccessor === Packed.AccessorType.SEGMENT) {
138-
return rhs && matchSegmentCondition(cmpKey, rhs, params);
141+
return rawRhs && matchSegmentCondition(cmpKey, rawRhs, params);
139142
}
140143

141-
const lhs = access(lhsAccessor, params);
144+
const rawLhs = access(lhsAccessor, params);
145+
const lhs = ci ? lower(rawLhs) : rawLhs;
146+
const rhs = ci ? lower(rawRhs) : rawRhs;
147+
142148
try {
143149
switch (cmpKey) {
144150
case Comparator.EQ:
145-
return ci ? lower(lhs) === lower(rhs) : lhs === rhs;
151+
return lhs === rhs;
146152
case Comparator.NOT_EQ:
147-
return ci ? lower(lhs) !== lower(rhs) : lhs !== rhs;
153+
return lhs !== rhs;
148154
case Comparator.ONE_OF:
149-
if (!isArray(rhs)) return false;
150-
return ci ? rhs.map(lower).includes(lower(lhs)) : rhs.includes(lhs);
155+
return isArray(rhs) && rhs.includes(lhs);
151156
case Comparator.NOT_ONE_OF:
157+
// lhs is undefined when the entity value was not provided, in which
158+
// case we should not match the rule
152159
if (!isArray(rhs) || typeof lhs === 'undefined') return false;
153-
return ci ? !rhs.map(lower).includes(lower(lhs)) : !rhs.includes(lhs);
160+
return !rhs.includes(lhs);
154161
case Comparator.CONTAINS_ALL_OF: {
155162
if (!Array.isArray(rhs) || !Array.isArray(lhs)) return false;
156163

157-
const lhsSet = new Set(
158-
ci
159-
? lhs.filter(isString).map((s) => s.toLowerCase())
160-
: lhs.filter(isString),
161-
);
164+
const lhsSet = new Set(lhs.filter(isString));
162165

163166
if (lhsSet.size === lhs.length) {
164-
return rhs
165-
.filter(isString)
166-
.every((item) => lhsSet.has(ci ? item.toLowerCase() : item));
167+
return rhs.filter(isString).every((item) => lhsSet.has(item));
167168
}
168169

169-
return ci
170-
? rhs.every((item) =>
171-
lhs.some(
172-
(l) =>
173-
isString(l) &&
174-
isString(item) &&
175-
l.toLowerCase() === item.toLowerCase(),
176-
),
177-
)
178-
: rhs.every((item) => lhs.includes(item));
170+
return rhs.every((item) => lhs.includes(item));
179171
}
180172
case Comparator.CONTAINS_ANY_OF: {
181173
if (!Array.isArray(rhs) || !Array.isArray(lhs)) return false;
182174

183-
if (ci) {
184-
const rhsSet = new Set(
185-
rhs.filter(isString).map((s) => s.toLowerCase()),
186-
);
187-
return lhs
188-
.filter(isString)
189-
.some((item) => rhsSet.has(item.toLowerCase()));
190-
}
191-
192175
const rhsSet = new Set(rhs.filter(isString));
193176
return lhs.some(
194177
rhsSet.size === rhs.length
@@ -200,15 +183,6 @@ function matchConditions<T>(
200183
if (!Array.isArray(rhs)) return false;
201184
if (!Array.isArray(lhs)) return true;
202185

203-
if (ci) {
204-
const rhsSet = new Set(
205-
rhs.filter(isString).map((s) => s.toLowerCase()),
206-
);
207-
return lhs
208-
.filter(isString)
209-
.every((item) => !rhsSet.has(item.toLowerCase()));
210-
}
211-
212186
const rhsSet = new Set(rhs.filter(isString));
213187
return lhs.every(
214188
rhsSet.size === rhs.length
@@ -217,93 +191,69 @@ function matchConditions<T>(
217191
);
218192
}
219193
case Comparator.STARTS_WITH:
220-
return ci
221-
? isString(lhs) &&
222-
isString(rhs) &&
223-
lhs.toLowerCase().startsWith(rhs.toLowerCase())
224-
: isString(lhs) && isString(rhs) && lhs.startsWith(rhs);
194+
return isString(lhs) && isString(rhs) && lhs.startsWith(rhs);
225195
case Comparator.NOT_STARTS_WITH:
226-
return ci
227-
? isString(lhs) &&
228-
isString(rhs) &&
229-
!lhs.toLowerCase().startsWith(rhs.toLowerCase())
230-
: isString(lhs) && isString(rhs) && !lhs.startsWith(rhs);
196+
return isString(lhs) && isString(rhs) && !lhs.startsWith(rhs);
231197
case Comparator.ENDS_WITH:
232-
return ci
233-
? isString(lhs) &&
234-
isString(rhs) &&
235-
lhs.toLowerCase().endsWith(rhs.toLowerCase())
236-
: isString(lhs) && isString(rhs) && lhs.endsWith(rhs);
198+
return isString(lhs) && isString(rhs) && lhs.endsWith(rhs);
237199
case Comparator.NOT_ENDS_WITH:
238-
return ci
239-
? isString(lhs) &&
240-
isString(rhs) &&
241-
!lhs.toLowerCase().endsWith(rhs.toLowerCase())
242-
: isString(lhs) && isString(rhs) && !lhs.endsWith(rhs);
200+
return isString(lhs) && isString(rhs) && !lhs.endsWith(rhs);
243201
case Comparator.CONTAINS:
244-
return ci
245-
? isString(lhs) &&
246-
isString(rhs) &&
247-
lhs.toLowerCase().includes(rhs.toLowerCase())
248-
: isString(lhs) && isString(rhs) && lhs.includes(rhs);
202+
return isString(lhs) && isString(rhs) && lhs.includes(rhs);
249203
case Comparator.NOT_CONTAINS:
250-
return ci
251-
? isString(lhs) &&
252-
isString(rhs) &&
253-
!lhs.toLowerCase().includes(rhs.toLowerCase())
254-
: isString(lhs) && isString(rhs) && !lhs.includes(rhs);
204+
return isString(lhs) && isString(rhs) && !lhs.includes(rhs);
255205
case Comparator.EXISTS:
256-
return lhs !== undefined && lhs !== null;
206+
return rawLhs !== undefined && rawLhs !== null;
257207
case Comparator.NOT_EXISTS:
258-
return lhs === undefined || lhs === null;
208+
return rawLhs === undefined || rawLhs === null;
259209
case Comparator.GT:
260210
// NaN will return false for any comparisons
261-
if (lhs === null || lhs === undefined) return false;
262-
return (isNumber(rhs) || isString(rhs)) && lhs > rhs;
211+
if (rawLhs === null || rawLhs === undefined) return false;
212+
return (isNumber(rawRhs) || isString(rawRhs)) && rawLhs > rawRhs;
263213
case Comparator.GTE:
264-
if (lhs === null || lhs === undefined) return false;
265-
return (isNumber(rhs) || isString(rhs)) && lhs >= rhs;
214+
if (rawLhs === null || rawLhs === undefined) return false;
215+
return (isNumber(rawRhs) || isString(rawRhs)) && rawLhs >= rawRhs;
266216
case Comparator.LT:
267-
if (lhs === null || lhs === undefined) return false;
268-
return (isNumber(rhs) || isString(rhs)) && lhs < rhs;
217+
if (rawLhs === null || rawLhs === undefined) return false;
218+
return (isNumber(rawRhs) || isString(rawRhs)) && rawLhs < rawRhs;
269219
case Comparator.LTE:
270-
if (lhs === null || lhs === undefined) return false;
271-
return (isNumber(rhs) || isString(rhs)) && lhs <= rhs;
220+
if (rawLhs === null || rawLhs === undefined) return false;
221+
return (isNumber(rawRhs) || isString(rawRhs)) && rawLhs <= rawRhs;
272222
case Comparator.REGEX:
273223
if (
274-
isString(lhs) &&
275-
lhs.length <= MAX_REGEX_INPUT_LENGTH &&
276-
typeof rhs === 'object' &&
277-
!Array.isArray(rhs) &&
278-
rhs?.type === 'regex'
224+
isString(rawLhs) &&
225+
rawLhs.length <= MAX_REGEX_INPUT_LENGTH &&
226+
typeof rawRhs === 'object' &&
227+
!Array.isArray(rawRhs) &&
228+
rawRhs?.type === 'regex'
279229
) {
280-
return new RegExp(rhs.pattern, rhs.flags).test(lhs);
230+
return new RegExp(rawRhs.pattern, rawRhs.flags).test(rawLhs);
281231
}
282232
return false;
283233

284234
case Comparator.NOT_REGEX:
285235
if (
286-
isString(lhs) &&
287-
lhs.length <= MAX_REGEX_INPUT_LENGTH &&
288-
typeof rhs === 'object' &&
289-
!Array.isArray(rhs) &&
290-
rhs?.type === 'regex'
236+
isString(rawLhs) &&
237+
rawLhs.length <= MAX_REGEX_INPUT_LENGTH &&
238+
typeof rawRhs === 'object' &&
239+
!Array.isArray(rawRhs) &&
240+
rawRhs?.type === 'regex'
291241
) {
292-
return !new RegExp(rhs.pattern, rhs.flags).test(lhs);
242+
return !new RegExp(rawRhs.pattern, rawRhs.flags).test(rawLhs);
293243
}
294244
return false;
295245
case Comparator.BEFORE: {
296-
if (!isString(lhs) || !isString(rhs)) return false;
297-
const a = new Date(lhs);
298-
const b = new Date(rhs);
246+
if (!isString(rawLhs) || !isString(rawRhs)) return false;
247+
const a = new Date(rawLhs);
248+
const b = new Date(rawRhs);
299249
// if any date fails to parse getTime will return NaN, which will cause
300250
// comparisons to fail.
301251
return a.getTime() < b.getTime();
302252
}
303253
case Comparator.AFTER: {
304-
if (!isString(lhs) || !isString(rhs)) return false;
305-
const a = new Date(lhs);
306-
const b = new Date(rhs);
254+
if (!isString(rawLhs) || !isString(rawRhs)) return false;
255+
const a = new Date(rawLhs);
256+
const b = new Date(rawRhs);
307257
return a.getTime() > b.getTime();
308258
}
309259
default: {

0 commit comments

Comments
 (0)