posthog-node 5.34.4 → 5.34.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/extensions/feature-flags/feature-flags.d.ts.map +1 -1
- package/dist/extensions/feature-flags/feature-flags.js +21 -19
- package/dist/extensions/feature-flags/feature-flags.mjs +21 -19
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/version.mjs +1 -1
- package/package.json +2 -2
- package/src/extensions/feature-flags/feature-flags.ts +59 -41
- package/src/version.ts +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"feature-flags.d.ts","sourceRoot":"","sources":["../../../src/extensions/feature-flags/feature-flags.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAmC,kBAAkB,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AACtH,OAAO,KAAK,EAAE,gBAAgB,EAAE,QAAQ,EAAE,mBAAmB,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAA;AAG1G,OAAO,EAAE,2BAA2B,EAA2B,MAAM,SAAS,CAAA;
|
|
1
|
+
{"version":3,"file":"feature-flags.d.ts","sourceRoot":"","sources":["../../../src/extensions/feature-flags/feature-flags.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAmC,kBAAkB,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AACtH,OAAO,KAAK,EAAE,gBAAgB,EAAE,QAAQ,EAAE,mBAAmB,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAA;AAG1G,OAAO,EAAE,2BAA2B,EAA2B,MAAM,SAAS,CAAA;AAW9E,cAAM,WAAY,SAAQ,KAAK;gBACjB,OAAO,EAAE,MAAM;CAO5B;AAED,cAAM,sBAAuB,SAAQ,KAAK;gBAC5B,OAAO,EAAE,MAAM;CAS5B;AAED,cAAM,wBAAyB,SAAQ,KAAK;gBAC9B,OAAO,EAAE,MAAM;CAS5B;AAED,KAAK,yBAAyB,GAAG;IAC/B,cAAc,EAAE,MAAM,CAAA;IACtB,aAAa,EAAE,MAAM,CAAA;IACrB,IAAI,EAAE,MAAM,CAAA;IACZ,eAAe,EAAE,MAAM,CAAA;IACvB,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,KAAK,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,mBAAmB,KAAK,OAAO,CAAC,oBAAoB,CAAC,CAAA;IACpF,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAA;IAChC,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;IAChC,aAAa,CAAC,EAAE;QAAE,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAA;KAAE,CAAA;IACzC,aAAa,CAAC,EAAE,2BAA2B,CAAA;IAC3C,qBAAqB,CAAC,EAAE,OAAO,CAAA;CAChC,CAAA;AAED,MAAM,MAAM,4BAA4B,GAAG;IACzC,UAAU,EAAE,MAAM,CAAA;IAClB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAC9B,gBAAgB,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;IACrC,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,CAAA;IACpD,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAA;CAClD,CAAA;AAED,KAAK,4BAA4B,GAAG;IAClC,UAAU,CAAC,EAAE,gBAAgB,CAAA;IAC7B,aAAa,CAAC,EAAE,OAAO,CAAA;CACxB,CAAA;AAED,cAAM,kBAAkB;IACtB,eAAe,EAAE,MAAM,CAAA;IACvB,cAAc,EAAE,MAAM,CAAA;IACtB,aAAa,EAAE,MAAM,CAAA;IACrB,YAAY,EAAE,KAAK,CAAC,kBAAkB,CAAC,CAAA;IACvC,iBAAiB,EAAE,MAAM,CAAC,MAAM,EAAE,kBAAkB,CAAC,CAAA;IACrD,gBAAgB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IACxC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,CAAA;IACtC,sBAAsB,EAAE,OAAO,CAAA;IAC/B,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,IAAI,EAAE,yBAAyB,CAAC,MAAM,CAAC,CAAA;IACvC,MAAM,CAAC,EAAE,MAAM,CAAC,OAAO,CAAA;IACvB,KAAK,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,mBAAmB,KAAK,OAAO,CAAC,oBAAoB,CAAC,CAAA;IACnF,SAAS,EAAE,OAAO,CAAQ;IAC1B,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAA;IAChC,aAAa,CAAC,EAAE;QAAE,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAA;KAAE,CAAA;IACzC,6BAA6B,EAAE,OAAO,CAAQ;IAC9C,YAAY,EAAE,MAAM,CAAI;IACxB,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;IAChC,OAAO,CAAC,aAAa,CAAC,CAA6B;IACnD,OAAO,CAAC,cAAc,CAAC,CAAe;IACtC,OAAO,CAAC,SAAS,CAAC,CAAQ;IAC1B,OAAO,CAAC,kBAAkB,CAAC,CAAQ;IACnC,OAAO,CAAC,qBAAqB,CAAS;IACtC,OAAO,CAAC,uBAAuB,CAAC,CAAQ;gBAE5B,EACV,eAAe,EACf,cAAc,EACd,aAAa,EACb,OAAO,EACP,IAAI,EACJ,aAAa,EACb,GAAG,OAAO,EACX,EAAE,yBAAyB;IAqB5B,KAAK,CAAC,OAAO,GAAE,OAAc,GAAG,IAAI;IAIpC,OAAO,CAAC,aAAa;IAMrB,OAAO,CAAC,uBAAuB;IAgBzB,cAAc,CAClB,GAAG,EAAE,MAAM,EACX,UAAU,EAAE,MAAM,EAClB,MAAM,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAM,EACnC,gBAAgB,GAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAM,EAC1C,eAAe,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAM,GACxD,OAAO,CAAC,gBAAgB,GAAG,SAAS,CAAC;IA8BlC,sBAAsB,CAC1B,iBAAiB,EAAE,4BAA4B,EAC/C,4BAA4B,CAAC,EAAE,MAAM,EAAE,GACtC,OAAO,CAAC;QACT,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAA;QAC1C,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAA;QAClC,eAAe,EAAE,OAAO,CAAA;KACzB,CAAC;IAyCI,4BAA4B,CAChC,IAAI,EAAE,kBAAkB,EACxB,iBAAiB,EAAE,4BAA4B,EAC/C,OAAO,GAAE,4BAAiC,GACzC,OAAO,CAAC;QACT,KAAK,EAAE,gBAAgB,CAAA;QACvB,OAAO,EAAE,QAAQ,GAAG,IAAI,CAAA;KACzB,CAAC;YA2BY,uBAAuB;IAkErC,OAAO,CAAC,wBAAwB;IAuBhC,OAAO,CAAC,qBAAqB;YA+Bf,sBAAsB;IAwEpC,OAAO,CAAC,4BAA4B;IAkB9B,0BAA0B,CAC9B,IAAI,EAAE,kBAAkB,EACxB,cAAc,EAAE,MAAM,EACtB,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAC/B,iBAAiB,EAAE,4BAA4B,GAC9C,OAAO,CAAC,gBAAgB,CAAC;IAgFtB,gBAAgB,CACpB,IAAI,EAAE,kBAAkB,EACxB,cAAc,EAAE,MAAM,EACtB,SAAS,EAAE,oBAAoB,EAC/B,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAC/B,iBAAiB,EAAE,4BAA4B,GAC9C,OAAO,CAAC,OAAO,CAAC;IAqCb,kBAAkB,CAAC,IAAI,EAAE,kBAAkB,EAAE,cAAc,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,GAAG,SAAS,CAAC;IAYjH,kBAAkB,CAAC,IAAI,EAAE,kBAAkB,GAAG;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,EAAE;IAkBnG;;OAEG;IACH,OAAO,CAAC,eAAe;IAWvB;;;;OAIG;IACH,OAAO,CAAC,kCAAkC;IAoB1C;;;OAGG;YACW,aAAa;IAqBrB,gBAAgB,CAAC,WAAW,UAAQ,GAAG,OAAO,CAAC,IAAI,CAAC;IAuB1D;;;OAGG;IACH,sBAAsB,IAAI,OAAO;IAIjC;;;OAGG;IACH,0BAA0B,IAAI,MAAM,GAAG,SAAS;IAIhD;;;;;OAKG;IACH,OAAO,CAAC,kBAAkB;IAQ1B;;;OAGG;IACH,OAAO,CAAC,YAAY;IAQpB;;;OAGG;IACH,OAAO,CAAC,YAAY;IAMd,iBAAiB,IAAI,OAAO,CAAC,IAAI,CAAC;IA8JxC,OAAO,CAAC,+BAA+B;IAoBvC,8BAA8B,IAAI,OAAO,CAAC,oBAAoB,CAAC;IAyBzD,UAAU,CAAC,SAAS,GAAE,MAAc,GAAG,OAAO,CAAC,IAAI,CAAC;CAuB3D;AAWD,iBAAS,aAAa,CACpB,QAAQ,EAAE,oBAAoB,CAAC,YAAY,CAAC,CAAC,MAAM,CAAC,EACpD,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EACnC,YAAY,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,GACnC,OAAO,CAqJT;AA6JD,KAAK,WAAW,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAA;AAE3C;;;;;;;;GAQG;AACH,iBAAS,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,WAAW,CA2B/C;AAsGD,iBAAS,uCAAuC,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,CAmC3E;AAED,OAAO,EACL,kBAAkB,EAClB,aAAa,EACb,uCAAuC,EACvC,WAAW,EACX,sBAAsB,EACtB,wBAAwB,EACxB,WAAW,GACZ,CAAA"}
|
|
@@ -37,7 +37,8 @@ const external_crypto_js_namespaceObject = require("./crypto.js");
|
|
|
37
37
|
const SIXTY_SECONDS = 60000;
|
|
38
38
|
const LONG_SCALE = 0xfffffffffffffff;
|
|
39
39
|
const NULL_VALUES_ALLOWED_OPERATORS = [
|
|
40
|
-
'is_not'
|
|
40
|
+
'is_not',
|
|
41
|
+
'is_set'
|
|
41
42
|
];
|
|
42
43
|
class ClientError extends Error {
|
|
43
44
|
constructor(message){
|
|
@@ -166,8 +167,8 @@ class FeatureFlagsPoller {
|
|
|
166
167
|
}
|
|
167
168
|
async computeFlagValueLocally(flag, evaluationContext) {
|
|
168
169
|
const { distinctId, groups, personProperties, groupProperties } = evaluationContext;
|
|
169
|
-
if (flag.ensure_experience_continuity) throw new InconclusiveMatchError('Flag has experience continuity enabled');
|
|
170
170
|
if (!flag.active) return false;
|
|
171
|
+
if (flag.ensure_experience_continuity) throw new InconclusiveMatchError('Flag has experience continuity enabled');
|
|
171
172
|
const flagFilters = flag.filters || {};
|
|
172
173
|
const aggregation_group_type_index = flagFilters.aggregation_group_type_index;
|
|
173
174
|
if (void 0 != aggregation_group_type_index) {
|
|
@@ -298,7 +299,7 @@ class FeatureFlagsPoller {
|
|
|
298
299
|
for (const prop of condition.properties){
|
|
299
300
|
const propertyType = prop.type;
|
|
300
301
|
let matches = false;
|
|
301
|
-
matches = 'cohort' === propertyType ? matchCohort(prop, properties, this.cohorts, this.debugMode) : 'flag' === propertyType ? await this.evaluateFlagDependency(prop, properties, evaluationContext) : matchProperty(prop, properties, warnFunction);
|
|
302
|
+
matches = 'cohort' === propertyType ? await matchCohort(prop, properties, this.cohorts, this.debugMode, (depProp)=>this.evaluateFlagDependency(depProp, properties, evaluationContext)) : 'flag' === propertyType ? await this.evaluateFlagDependency(prop, properties, evaluationContext) : matchProperty(prop, properties, warnFunction);
|
|
302
303
|
if (!matches) return false;
|
|
303
304
|
}
|
|
304
305
|
if (void 0 == rolloutPercentage) return true;
|
|
@@ -509,8 +510,11 @@ function matchProperty(property, propertyValues, warnFunction) {
|
|
|
509
510
|
const value = property.value;
|
|
510
511
|
const operator = property.operator || 'exact';
|
|
511
512
|
if (key in propertyValues) {
|
|
512
|
-
if ('is_not_set' === operator)
|
|
513
|
-
} else
|
|
513
|
+
if ('is_not_set' === operator) return false;
|
|
514
|
+
} else {
|
|
515
|
+
if ('is_not_set' === operator) return true;
|
|
516
|
+
throw new InconclusiveMatchError(`Property ${key} not found in propertyValues`);
|
|
517
|
+
}
|
|
514
518
|
const overrideValue = propertyValues[key];
|
|
515
519
|
if (null == overrideValue && !NULL_VALUES_ALLOWED_OPERATORS.includes(operator)) {
|
|
516
520
|
if (warnFunction) warnFunction(`Property ${key} cannot have a value of null/undefined with the ${operator} operator`);
|
|
@@ -547,13 +551,11 @@ function matchProperty(property, propertyValues, warnFunction) {
|
|
|
547
551
|
case 'lt':
|
|
548
552
|
case 'lte':
|
|
549
553
|
{
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
if ('string' == typeof overrideValue) return compare(overrideValue, String(value), operator);
|
|
556
|
-
return compare(overrideValue, parsedValue, operator);
|
|
554
|
+
const parsedValue = 'number' == typeof value ? value : parseFloat(String(value));
|
|
555
|
+
let parsedOverride;
|
|
556
|
+
parsedOverride = 'number' == typeof overrideValue ? overrideValue : null != overrideValue ? parseFloat(String(overrideValue)) : NaN;
|
|
557
|
+
if (Number.isFinite(parsedValue) && Number.isFinite(parsedOverride)) return compare(parsedOverride, parsedValue, operator);
|
|
558
|
+
return compare(String(overrideValue), String(value), operator);
|
|
557
559
|
}
|
|
558
560
|
case 'is_date_after':
|
|
559
561
|
case 'is_date_before':
|
|
@@ -623,13 +625,13 @@ function matchProperty(property, propertyValues, warnFunction) {
|
|
|
623
625
|
function checkCohortExists(cohortId, cohortProperties) {
|
|
624
626
|
if (!(cohortId in cohortProperties)) throw new RequiresServerEvaluation(`cohort ${cohortId} not found in local cohorts - likely a static cohort that requires server evaluation`);
|
|
625
627
|
}
|
|
626
|
-
function matchCohort(property, propertyValues, cohortProperties, debugMode = false) {
|
|
628
|
+
async function matchCohort(property, propertyValues, cohortProperties, debugMode = false, flagDependencyEvaluator) {
|
|
627
629
|
const cohortId = String(property.value);
|
|
628
630
|
checkCohortExists(cohortId, cohortProperties);
|
|
629
631
|
const propertyGroup = cohortProperties[cohortId];
|
|
630
|
-
return matchPropertyGroup(propertyGroup, propertyValues, cohortProperties, debugMode);
|
|
632
|
+
return matchPropertyGroup(propertyGroup, propertyValues, cohortProperties, debugMode, flagDependencyEvaluator);
|
|
631
633
|
}
|
|
632
|
-
function matchPropertyGroup(propertyGroup, propertyValues, cohortProperties, debugMode = false) {
|
|
634
|
+
async function matchPropertyGroup(propertyGroup, propertyValues, cohortProperties, debugMode = false, flagDependencyEvaluator) {
|
|
633
635
|
if (!propertyGroup) return true;
|
|
634
636
|
const propertyGroupType = propertyGroup.type;
|
|
635
637
|
const properties = propertyGroup.values;
|
|
@@ -637,7 +639,7 @@ function matchPropertyGroup(propertyGroup, propertyValues, cohortProperties, deb
|
|
|
637
639
|
let errorMatchingLocally = false;
|
|
638
640
|
if ('values' in properties[0]) {
|
|
639
641
|
for (const prop of properties)try {
|
|
640
|
-
const matches = matchPropertyGroup(prop, propertyValues, cohortProperties, debugMode);
|
|
642
|
+
const matches = await matchPropertyGroup(prop, propertyValues, cohortProperties, debugMode, flagDependencyEvaluator);
|
|
641
643
|
if ('AND' === propertyGroupType) {
|
|
642
644
|
if (!matches) return false;
|
|
643
645
|
} else if (matches) return true;
|
|
@@ -653,10 +655,10 @@ function matchPropertyGroup(propertyGroup, propertyValues, cohortProperties, deb
|
|
|
653
655
|
}
|
|
654
656
|
for (const prop of properties)try {
|
|
655
657
|
let matches;
|
|
656
|
-
if ('cohort' === prop.type) matches = matchCohort(prop, propertyValues, cohortProperties, debugMode);
|
|
658
|
+
if ('cohort' === prop.type) matches = await matchCohort(prop, propertyValues, cohortProperties, debugMode, flagDependencyEvaluator);
|
|
657
659
|
else if ('flag' === prop.type) {
|
|
658
|
-
if (
|
|
659
|
-
|
|
660
|
+
if (!flagDependencyEvaluator) throw new InconclusiveMatchError(`Flag dependency '${prop.key || 'unknown'}' cannot be evaluated without a flag dependency evaluator`);
|
|
661
|
+
matches = await flagDependencyEvaluator(prop);
|
|
660
662
|
} else matches = matchProperty(prop, propertyValues);
|
|
661
663
|
const negation = prop.negation || false;
|
|
662
664
|
if ('AND' === propertyGroupType) {
|
|
@@ -3,7 +3,8 @@ import { hashSHA1 } from "./crypto.mjs";
|
|
|
3
3
|
const SIXTY_SECONDS = 60000;
|
|
4
4
|
const LONG_SCALE = 0xfffffffffffffff;
|
|
5
5
|
const NULL_VALUES_ALLOWED_OPERATORS = [
|
|
6
|
-
'is_not'
|
|
6
|
+
'is_not',
|
|
7
|
+
'is_set'
|
|
7
8
|
];
|
|
8
9
|
class ClientError extends Error {
|
|
9
10
|
constructor(message){
|
|
@@ -132,8 +133,8 @@ class FeatureFlagsPoller {
|
|
|
132
133
|
}
|
|
133
134
|
async computeFlagValueLocally(flag, evaluationContext) {
|
|
134
135
|
const { distinctId, groups, personProperties, groupProperties } = evaluationContext;
|
|
135
|
-
if (flag.ensure_experience_continuity) throw new InconclusiveMatchError('Flag has experience continuity enabled');
|
|
136
136
|
if (!flag.active) return false;
|
|
137
|
+
if (flag.ensure_experience_continuity) throw new InconclusiveMatchError('Flag has experience continuity enabled');
|
|
137
138
|
const flagFilters = flag.filters || {};
|
|
138
139
|
const aggregation_group_type_index = flagFilters.aggregation_group_type_index;
|
|
139
140
|
if (void 0 != aggregation_group_type_index) {
|
|
@@ -264,7 +265,7 @@ class FeatureFlagsPoller {
|
|
|
264
265
|
for (const prop of condition.properties){
|
|
265
266
|
const propertyType = prop.type;
|
|
266
267
|
let matches = false;
|
|
267
|
-
matches = 'cohort' === propertyType ? matchCohort(prop, properties, this.cohorts, this.debugMode) : 'flag' === propertyType ? await this.evaluateFlagDependency(prop, properties, evaluationContext) : matchProperty(prop, properties, warnFunction);
|
|
268
|
+
matches = 'cohort' === propertyType ? await matchCohort(prop, properties, this.cohorts, this.debugMode, (depProp)=>this.evaluateFlagDependency(depProp, properties, evaluationContext)) : 'flag' === propertyType ? await this.evaluateFlagDependency(prop, properties, evaluationContext) : matchProperty(prop, properties, warnFunction);
|
|
268
269
|
if (!matches) return false;
|
|
269
270
|
}
|
|
270
271
|
if (void 0 == rolloutPercentage) return true;
|
|
@@ -475,8 +476,11 @@ function matchProperty(property, propertyValues, warnFunction) {
|
|
|
475
476
|
const value = property.value;
|
|
476
477
|
const operator = property.operator || 'exact';
|
|
477
478
|
if (key in propertyValues) {
|
|
478
|
-
if ('is_not_set' === operator)
|
|
479
|
-
} else
|
|
479
|
+
if ('is_not_set' === operator) return false;
|
|
480
|
+
} else {
|
|
481
|
+
if ('is_not_set' === operator) return true;
|
|
482
|
+
throw new InconclusiveMatchError(`Property ${key} not found in propertyValues`);
|
|
483
|
+
}
|
|
480
484
|
const overrideValue = propertyValues[key];
|
|
481
485
|
if (null == overrideValue && !NULL_VALUES_ALLOWED_OPERATORS.includes(operator)) {
|
|
482
486
|
if (warnFunction) warnFunction(`Property ${key} cannot have a value of null/undefined with the ${operator} operator`);
|
|
@@ -513,13 +517,11 @@ function matchProperty(property, propertyValues, warnFunction) {
|
|
|
513
517
|
case 'lt':
|
|
514
518
|
case 'lte':
|
|
515
519
|
{
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
if ('string' == typeof overrideValue) return compare(overrideValue, String(value), operator);
|
|
522
|
-
return compare(overrideValue, parsedValue, operator);
|
|
520
|
+
const parsedValue = 'number' == typeof value ? value : parseFloat(String(value));
|
|
521
|
+
let parsedOverride;
|
|
522
|
+
parsedOverride = 'number' == typeof overrideValue ? overrideValue : null != overrideValue ? parseFloat(String(overrideValue)) : NaN;
|
|
523
|
+
if (Number.isFinite(parsedValue) && Number.isFinite(parsedOverride)) return compare(parsedOverride, parsedValue, operator);
|
|
524
|
+
return compare(String(overrideValue), String(value), operator);
|
|
523
525
|
}
|
|
524
526
|
case 'is_date_after':
|
|
525
527
|
case 'is_date_before':
|
|
@@ -589,13 +591,13 @@ function matchProperty(property, propertyValues, warnFunction) {
|
|
|
589
591
|
function checkCohortExists(cohortId, cohortProperties) {
|
|
590
592
|
if (!(cohortId in cohortProperties)) throw new RequiresServerEvaluation(`cohort ${cohortId} not found in local cohorts - likely a static cohort that requires server evaluation`);
|
|
591
593
|
}
|
|
592
|
-
function matchCohort(property, propertyValues, cohortProperties, debugMode = false) {
|
|
594
|
+
async function matchCohort(property, propertyValues, cohortProperties, debugMode = false, flagDependencyEvaluator) {
|
|
593
595
|
const cohortId = String(property.value);
|
|
594
596
|
checkCohortExists(cohortId, cohortProperties);
|
|
595
597
|
const propertyGroup = cohortProperties[cohortId];
|
|
596
|
-
return matchPropertyGroup(propertyGroup, propertyValues, cohortProperties, debugMode);
|
|
598
|
+
return matchPropertyGroup(propertyGroup, propertyValues, cohortProperties, debugMode, flagDependencyEvaluator);
|
|
597
599
|
}
|
|
598
|
-
function matchPropertyGroup(propertyGroup, propertyValues, cohortProperties, debugMode = false) {
|
|
600
|
+
async function matchPropertyGroup(propertyGroup, propertyValues, cohortProperties, debugMode = false, flagDependencyEvaluator) {
|
|
599
601
|
if (!propertyGroup) return true;
|
|
600
602
|
const propertyGroupType = propertyGroup.type;
|
|
601
603
|
const properties = propertyGroup.values;
|
|
@@ -603,7 +605,7 @@ function matchPropertyGroup(propertyGroup, propertyValues, cohortProperties, deb
|
|
|
603
605
|
let errorMatchingLocally = false;
|
|
604
606
|
if ('values' in properties[0]) {
|
|
605
607
|
for (const prop of properties)try {
|
|
606
|
-
const matches = matchPropertyGroup(prop, propertyValues, cohortProperties, debugMode);
|
|
608
|
+
const matches = await matchPropertyGroup(prop, propertyValues, cohortProperties, debugMode, flagDependencyEvaluator);
|
|
607
609
|
if ('AND' === propertyGroupType) {
|
|
608
610
|
if (!matches) return false;
|
|
609
611
|
} else if (matches) return true;
|
|
@@ -619,10 +621,10 @@ function matchPropertyGroup(propertyGroup, propertyValues, cohortProperties, deb
|
|
|
619
621
|
}
|
|
620
622
|
for (const prop of properties)try {
|
|
621
623
|
let matches;
|
|
622
|
-
if ('cohort' === prop.type) matches = matchCohort(prop, propertyValues, cohortProperties, debugMode);
|
|
624
|
+
if ('cohort' === prop.type) matches = await matchCohort(prop, propertyValues, cohortProperties, debugMode, flagDependencyEvaluator);
|
|
623
625
|
else if ('flag' === prop.type) {
|
|
624
|
-
if (
|
|
625
|
-
|
|
626
|
+
if (!flagDependencyEvaluator) throw new InconclusiveMatchError(`Flag dependency '${prop.key || 'unknown'}' cannot be evaluated without a flag dependency evaluator`);
|
|
627
|
+
matches = await flagDependencyEvaluator(prop);
|
|
626
628
|
} else matches = matchProperty(prop, propertyValues);
|
|
627
629
|
const negation = prop.negation || false;
|
|
628
630
|
if ('AND' === propertyGroupType) {
|
package/dist/version.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export declare const version = "5.34.
|
|
1
|
+
export declare const version = "5.34.6";
|
|
2
2
|
//# sourceMappingURL=version.d.ts.map
|
package/dist/version.js
CHANGED
|
@@ -26,7 +26,7 @@ __webpack_require__.r(__webpack_exports__);
|
|
|
26
26
|
__webpack_require__.d(__webpack_exports__, {
|
|
27
27
|
version: ()=>version
|
|
28
28
|
});
|
|
29
|
-
const version = '5.34.
|
|
29
|
+
const version = '5.34.6';
|
|
30
30
|
exports.version = __webpack_exports__.version;
|
|
31
31
|
for(var __webpack_i__ in __webpack_exports__)if (-1 === [
|
|
32
32
|
"version"
|
package/dist/version.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
const version = '5.34.
|
|
1
|
+
const version = '5.34.6';
|
|
2
2
|
export { version };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "posthog-node",
|
|
3
|
-
"version": "5.34.
|
|
3
|
+
"version": "5.34.6",
|
|
4
4
|
"description": "PostHog Node.js integration",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
"module": "dist/entrypoints/index.node.mjs",
|
|
26
26
|
"types": "dist/entrypoints/index.node.d.ts",
|
|
27
27
|
"dependencies": {
|
|
28
|
-
"@posthog/core": "1.29.
|
|
28
|
+
"@posthog/core": "1.29.5"
|
|
29
29
|
},
|
|
30
30
|
"devDependencies": {
|
|
31
31
|
"@edge-runtime/jest-environment": "^4.0.0",
|
|
@@ -9,7 +9,10 @@ const SIXTY_SECONDS = 60 * 1000
|
|
|
9
9
|
// eslint-disable-next-line
|
|
10
10
|
const LONG_SCALE = 0xfffffffffffffff
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
// Operators that should still run their switch case when the property value is null/undefined.
|
|
13
|
+
// `is_not` may legitimately compare against null; `is_set` only cares about key presence and
|
|
14
|
+
// must not be short-circuited by the null guard in `matchProperty`.
|
|
15
|
+
const NULL_VALUES_ALLOWED_OPERATORS = ['is_not', 'is_set']
|
|
13
16
|
class ClientError extends Error {
|
|
14
17
|
constructor(message: string) {
|
|
15
18
|
super()
|
|
@@ -276,14 +279,17 @@ class FeatureFlagsPoller {
|
|
|
276
279
|
): Promise<FeatureFlagValue> {
|
|
277
280
|
const { distinctId, groups, personProperties, groupProperties } = evaluationContext
|
|
278
281
|
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
282
|
+
// Order matters: an inactive flag is always false regardless of continuity. Checking
|
|
283
|
+
// `ensure_experience_continuity` first would cause a disabled-but-continuity flag to come
|
|
284
|
+
// back as undefined instead of the correct `false`.
|
|
283
285
|
if (!flag.active) {
|
|
284
286
|
return false
|
|
285
287
|
}
|
|
286
288
|
|
|
289
|
+
if (flag.ensure_experience_continuity) {
|
|
290
|
+
throw new InconclusiveMatchError('Flag has experience continuity enabled')
|
|
291
|
+
}
|
|
292
|
+
|
|
287
293
|
const flagFilters = flag.filters || {}
|
|
288
294
|
const aggregation_group_type_index = flagFilters.aggregation_group_type_index
|
|
289
295
|
|
|
@@ -579,7 +585,9 @@ class FeatureFlagsPoller {
|
|
|
579
585
|
let matches = false
|
|
580
586
|
|
|
581
587
|
if (propertyType === 'cohort') {
|
|
582
|
-
matches = matchCohort(prop, properties, this.cohorts, this.debugMode)
|
|
588
|
+
matches = await matchCohort(prop, properties, this.cohorts, this.debugMode, (depProp) =>
|
|
589
|
+
this.evaluateFlagDependency(depProp, properties, evaluationContext)
|
|
590
|
+
)
|
|
583
591
|
} else if (propertyType === 'flag') {
|
|
584
592
|
matches = await this.evaluateFlagDependency(prop, properties, evaluationContext)
|
|
585
593
|
} else {
|
|
@@ -1019,9 +1027,14 @@ function matchProperty(
|
|
|
1019
1027
|
const operator = property.operator || 'exact'
|
|
1020
1028
|
|
|
1021
1029
|
if (!(key in propertyValues)) {
|
|
1030
|
+
// When the property is genuinely absent we can answer `is_not_set` locally — no need to
|
|
1031
|
+
// bail out as inconclusive and force the flag to return undefined.
|
|
1032
|
+
if (operator === 'is_not_set') {
|
|
1033
|
+
return true
|
|
1034
|
+
}
|
|
1022
1035
|
throw new InconclusiveMatchError(`Property ${key} not found in propertyValues`)
|
|
1023
1036
|
} else if (operator === 'is_not_set') {
|
|
1024
|
-
|
|
1037
|
+
return false
|
|
1025
1038
|
}
|
|
1026
1039
|
|
|
1027
1040
|
const overrideValue = propertyValues[key]
|
|
@@ -1075,28 +1088,24 @@ function matchProperty(
|
|
|
1075
1088
|
case 'gte':
|
|
1076
1089
|
case 'lt':
|
|
1077
1090
|
case 'lte': {
|
|
1078
|
-
//
|
|
1079
|
-
//
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
if (parsedValue != null && overrideValue != null) {
|
|
1091
|
-
// check both null and undefined
|
|
1092
|
-
if (typeof overrideValue === 'string') {
|
|
1093
|
-
return compare(overrideValue, String(value), operator)
|
|
1094
|
-
} else {
|
|
1095
|
-
return compare(overrideValue, parsedValue, operator)
|
|
1096
|
-
}
|
|
1091
|
+
// Try a numeric comparison first; only fall back to lexicographic when one side genuinely
|
|
1092
|
+
// isn't a number. `parseFloat` returns NaN for non-numeric strings, so `Number.isFinite`
|
|
1093
|
+
// is the right guard — `NaN != null` would slip through and produce nonsense comparisons
|
|
1094
|
+
// like `NaN > 5`. Likewise, when a person property arrives as the string `"10"` we want
|
|
1095
|
+
// `"10" > "9"` to evaluate numerically (true), not lexicographically (false).
|
|
1096
|
+
const parsedValue = typeof value === 'number' ? value : parseFloat(String(value))
|
|
1097
|
+
let parsedOverride: number
|
|
1098
|
+
if (typeof overrideValue === 'number') {
|
|
1099
|
+
parsedOverride = overrideValue
|
|
1100
|
+
} else if (overrideValue != null) {
|
|
1101
|
+
parsedOverride = parseFloat(String(overrideValue))
|
|
1097
1102
|
} else {
|
|
1098
|
-
|
|
1103
|
+
parsedOverride = NaN
|
|
1104
|
+
}
|
|
1105
|
+
if (Number.isFinite(parsedValue) && Number.isFinite(parsedOverride)) {
|
|
1106
|
+
return compare(parsedOverride, parsedValue, operator)
|
|
1099
1107
|
}
|
|
1108
|
+
return compare(String(overrideValue), String(value), operator)
|
|
1100
1109
|
}
|
|
1101
1110
|
case 'is_date_after':
|
|
1102
1111
|
case 'is_date_before': {
|
|
@@ -1171,25 +1180,29 @@ function checkCohortExists(cohortId: string, cohortProperties: FeatureFlagsPolle
|
|
|
1171
1180
|
}
|
|
1172
1181
|
}
|
|
1173
1182
|
|
|
1174
|
-
|
|
1183
|
+
type FlagDependencyEvaluator = (prop: FlagProperty) => Promise<boolean>
|
|
1184
|
+
|
|
1185
|
+
async function matchCohort(
|
|
1175
1186
|
property: FeatureFlagCondition['properties'][number],
|
|
1176
1187
|
propertyValues: Record<string, any>,
|
|
1177
1188
|
cohortProperties: FeatureFlagsPoller['cohorts'],
|
|
1178
|
-
debugMode: boolean = false
|
|
1179
|
-
|
|
1189
|
+
debugMode: boolean = false,
|
|
1190
|
+
flagDependencyEvaluator?: FlagDependencyEvaluator
|
|
1191
|
+
): Promise<boolean> {
|
|
1180
1192
|
const cohortId = String(property.value)
|
|
1181
1193
|
checkCohortExists(cohortId, cohortProperties)
|
|
1182
1194
|
|
|
1183
1195
|
const propertyGroup = cohortProperties[cohortId]
|
|
1184
|
-
return matchPropertyGroup(propertyGroup, propertyValues, cohortProperties, debugMode)
|
|
1196
|
+
return matchPropertyGroup(propertyGroup, propertyValues, cohortProperties, debugMode, flagDependencyEvaluator)
|
|
1185
1197
|
}
|
|
1186
1198
|
|
|
1187
|
-
function matchPropertyGroup(
|
|
1199
|
+
async function matchPropertyGroup(
|
|
1188
1200
|
propertyGroup: PropertyGroup,
|
|
1189
1201
|
propertyValues: Record<string, any>,
|
|
1190
1202
|
cohortProperties: FeatureFlagsPoller['cohorts'],
|
|
1191
|
-
debugMode: boolean = false
|
|
1192
|
-
|
|
1203
|
+
debugMode: boolean = false,
|
|
1204
|
+
flagDependencyEvaluator?: FlagDependencyEvaluator
|
|
1205
|
+
): Promise<boolean> {
|
|
1193
1206
|
if (!propertyGroup) {
|
|
1194
1207
|
return true
|
|
1195
1208
|
}
|
|
@@ -1208,7 +1221,13 @@ function matchPropertyGroup(
|
|
|
1208
1221
|
// a nested property group
|
|
1209
1222
|
for (const prop of properties as PropertyGroup[]) {
|
|
1210
1223
|
try {
|
|
1211
|
-
const matches = matchPropertyGroup(
|
|
1224
|
+
const matches = await matchPropertyGroup(
|
|
1225
|
+
prop,
|
|
1226
|
+
propertyValues,
|
|
1227
|
+
cohortProperties,
|
|
1228
|
+
debugMode,
|
|
1229
|
+
flagDependencyEvaluator
|
|
1230
|
+
)
|
|
1212
1231
|
if (propertyGroupType === 'AND') {
|
|
1213
1232
|
if (!matches) {
|
|
1214
1233
|
return false
|
|
@@ -1244,15 +1263,14 @@ function matchPropertyGroup(
|
|
|
1244
1263
|
try {
|
|
1245
1264
|
let matches: boolean
|
|
1246
1265
|
if (prop.type === 'cohort') {
|
|
1247
|
-
matches = matchCohort(prop, propertyValues, cohortProperties, debugMode)
|
|
1266
|
+
matches = await matchCohort(prop, propertyValues, cohortProperties, debugMode, flagDependencyEvaluator)
|
|
1248
1267
|
} else if (prop.type === 'flag') {
|
|
1249
|
-
if (
|
|
1250
|
-
|
|
1251
|
-
`
|
|
1252
|
-
`Skipping condition with dependency on flag '${prop.key || 'unknown'}'`
|
|
1268
|
+
if (!flagDependencyEvaluator) {
|
|
1269
|
+
throw new InconclusiveMatchError(
|
|
1270
|
+
`Flag dependency '${prop.key || 'unknown'}' cannot be evaluated without a flag dependency evaluator`
|
|
1253
1271
|
)
|
|
1254
1272
|
}
|
|
1255
|
-
|
|
1273
|
+
matches = await flagDependencyEvaluator(prop)
|
|
1256
1274
|
} else {
|
|
1257
1275
|
matches = matchProperty(prop, propertyValues)
|
|
1258
1276
|
}
|
package/src/version.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const version = '5.34.
|
|
1
|
+
export const version = '5.34.6'
|