posthog-node 2.5.3 → 2.6.0

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/CHANGELOG.md CHANGED
@@ -1,3 +1,10 @@
1
+ # 2.6.0 - 2023-03-14
2
+
3
+ 1. Add support for all cohorts local evaluation in feature flags.
4
+
5
+ # 2.5.4 - 2023-02-27
6
+
7
+ 1. Fix error log for local evaluation of feature flags (InconclusiveMatchError(s)) to only show during debug mode.
1
8
  # 2.5.3 - 2023-02-21
2
9
 
3
10
  1. Allow passing in a distinctId to `groupIdentify()`.
package/lib/index.cjs.js CHANGED
@@ -163,7 +163,7 @@ function __spreadArray(to, from, pack) {
163
163
  return to.concat(ar || Array.prototype.slice.call(from));
164
164
  }
165
165
 
166
- var version = "2.5.3";
166
+ var version = "2.6.0";
167
167
 
168
168
  var PostHogPersistedProperty;
169
169
  (function (PostHogPersistedProperty) {
@@ -725,6 +725,7 @@ var SimpleEventEmitter = /** @class */ (function () {
725
725
  var PostHogCoreStateless = /** @class */ (function () {
726
726
  function PostHogCoreStateless(apiKey, options) {
727
727
  var _a, _b, _c, _d;
728
+ this.debugMode = false;
728
729
  this.pendingPromises = {};
729
730
  // internal
730
731
  this._events = new SimpleEventEmitter();
@@ -769,6 +770,7 @@ var PostHogCoreStateless = /** @class */ (function () {
769
770
  var _a;
770
771
  if (enabled === void 0) { enabled = true; }
771
772
  (_a = this.removeDebugCallback) === null || _a === void 0 ? void 0 : _a.call(this);
773
+ this.debugMode = enabled;
772
774
  if (enabled) {
773
775
  this.removeDebugCallback = this.on('*', function (event, payload) { return console.log('PostHog Debug', event, payload); });
774
776
  }
@@ -1598,11 +1600,13 @@ function () {
1598
1600
  host = _a.host,
1599
1601
  options = __rest(_a, ["pollingInterval", "personalApiKey", "projectApiKey", "timeout", "host"]);
1600
1602
 
1603
+ this.debugMode = false;
1601
1604
  this.pollingInterval = pollingInterval;
1602
1605
  this.personalApiKey = personalApiKey;
1603
1606
  this.featureFlags = [];
1604
1607
  this.featureFlagsByKey = {};
1605
1608
  this.groupTypeMapping = {};
1609
+ this.cohorts = {};
1606
1610
  this.loadedSuccessfullyOnce = false;
1607
1611
  this.timeout = timeout;
1608
1612
  this.projectApiKey = projectApiKey;
@@ -1613,6 +1617,14 @@ function () {
1613
1617
  void this.loadFeatureFlags();
1614
1618
  }
1615
1619
 
1620
+ FeatureFlagsPoller.prototype.debug = function (enabled) {
1621
+ if (enabled === void 0) {
1622
+ enabled = true;
1623
+ }
1624
+
1625
+ this.debugMode = enabled;
1626
+ };
1627
+
1616
1628
  FeatureFlagsPoller.prototype.getFeatureFlag = function (key, distinctId, groups, personProperties, groupProperties) {
1617
1629
  if (groups === void 0) {
1618
1630
  groups = {};
@@ -1660,9 +1672,15 @@ function () {
1660
1672
  if (featureFlag !== undefined) {
1661
1673
  try {
1662
1674
  response = this.computeFlagLocally(featureFlag, distinctId, groups, personProperties, groupProperties);
1675
+
1676
+ if (this.debugMode) {
1677
+ console.debug("Successfully computed flag locally: ".concat(key, " -> ").concat(response));
1678
+ }
1663
1679
  } catch (e) {
1664
1680
  if (e instanceof InconclusiveMatchError) {
1665
- console.error("InconclusiveMatchError when computing flag locally: ".concat(key, ": ").concat(e));
1681
+ if (this.debugMode) {
1682
+ console.debug("InconclusiveMatchError when computing flag locally: ".concat(key, ": ").concat(e));
1683
+ }
1666
1684
  } else if (e instanceof Error) {
1667
1685
  console.error("Error computing flag locally: ".concat(key, ": ").concat(e));
1668
1686
  }
@@ -1926,14 +1944,23 @@ function () {
1926
1944
  var rolloutPercentage = condition.rollout_percentage;
1927
1945
 
1928
1946
  if ((condition.properties || []).length > 0) {
1929
- var matchAll = condition.properties.every(function (property) {
1930
- return matchProperty(property, properties);
1931
- });
1947
+ for (var _i = 0, _a = condition.properties; _i < _a.length; _i++) {
1948
+ var prop = _a[_i];
1949
+ var propertyType = prop.type;
1950
+ var matches = false;
1932
1951
 
1933
- if (!matchAll) {
1934
- return false;
1935
- } else if (rolloutPercentage == undefined) {
1936
- // == to include `null` as a match, not just `undefined`
1952
+ if (propertyType === 'cohort') {
1953
+ matches = matchCohort(prop, properties, this.cohorts);
1954
+ } else {
1955
+ matches = matchProperty(prop, properties);
1956
+ }
1957
+
1958
+ if (!matches) {
1959
+ return false;
1960
+ }
1961
+ }
1962
+
1963
+ if (rolloutPercentage == undefined) {
1937
1964
  return true;
1938
1965
  }
1939
1966
  }
@@ -2066,6 +2093,7 @@ function () {
2066
2093
  return acc[curr.key] = curr, acc;
2067
2094
  }, {});
2068
2095
  this.groupTypeMapping = responseJson.group_type_mapping || {};
2096
+ this.cohorts = responseJson.cohorts || [];
2069
2097
  this.loadedSuccessfullyOnce = true;
2070
2098
  return [3
2071
2099
  /*break*/
@@ -2098,7 +2126,7 @@ function () {
2098
2126
  return __generator(this, function (_a) {
2099
2127
  switch (_a.label) {
2100
2128
  case 0:
2101
- url = "".concat(this.host, "/api/feature_flag/local_evaluation?token=").concat(this.projectApiKey);
2129
+ url = "".concat(this.host, "/api/feature_flag/local_evaluation?token=").concat(this.projectApiKey, "&send_cohorts");
2102
2130
  options = {
2103
2131
  method: 'GET',
2104
2132
  headers: {
@@ -2235,6 +2263,119 @@ function matchProperty(property, propertyValues) {
2235
2263
  }
2236
2264
  }
2237
2265
 
2266
+ function matchCohort(property, propertyValues, cohortProperties) {
2267
+ var cohortId = String(property.value);
2268
+
2269
+ if (!(cohortId in cohortProperties)) {
2270
+ throw new InconclusiveMatchError("can't match cohort without a given cohort property value");
2271
+ }
2272
+
2273
+ var propertyGroup = cohortProperties[cohortId];
2274
+ return matchPropertyGroup(propertyGroup, propertyValues, cohortProperties);
2275
+ }
2276
+
2277
+ function matchPropertyGroup(propertyGroup, propertyValues, cohortProperties) {
2278
+ if (!propertyGroup) {
2279
+ return true;
2280
+ }
2281
+
2282
+ var propertyGroupType = propertyGroup.type;
2283
+ var properties = propertyGroup.values;
2284
+
2285
+ if (!properties || properties.length === 0) {
2286
+ // empty groups are no-ops, always match
2287
+ return true;
2288
+ }
2289
+
2290
+ var errorMatchingLocally = false;
2291
+
2292
+ if ('values' in properties[0]) {
2293
+ // a nested property group
2294
+ for (var _i = 0, _a = properties; _i < _a.length; _i++) {
2295
+ var prop = _a[_i];
2296
+
2297
+ try {
2298
+ var matches = matchPropertyGroup(prop, propertyValues, cohortProperties);
2299
+
2300
+ if (propertyGroupType === 'AND') {
2301
+ if (!matches) {
2302
+ return false;
2303
+ }
2304
+ } else {
2305
+ // OR group
2306
+ if (matches) {
2307
+ return true;
2308
+ }
2309
+ }
2310
+ } catch (err) {
2311
+ if (err instanceof InconclusiveMatchError) {
2312
+ console.debug("Failed to compute property ".concat(prop, " locally: ").concat(err));
2313
+ errorMatchingLocally = true;
2314
+ } else {
2315
+ throw err;
2316
+ }
2317
+ }
2318
+ }
2319
+
2320
+ if (errorMatchingLocally) {
2321
+ throw new InconclusiveMatchError("Can't match cohort without a given cohort property value");
2322
+ } // if we get here, all matched in AND case, or none matched in OR case
2323
+
2324
+
2325
+ return propertyGroupType === 'AND';
2326
+ } else {
2327
+ for (var _b = 0, _c = properties; _b < _c.length; _b++) {
2328
+ var prop = _c[_b];
2329
+
2330
+ try {
2331
+ var matches = void 0;
2332
+
2333
+ if (prop.type === 'cohort') {
2334
+ matches = matchCohort(prop, propertyValues, cohortProperties);
2335
+ } else {
2336
+ matches = matchProperty(prop, propertyValues);
2337
+ }
2338
+
2339
+ var negation = prop.negation || false;
2340
+
2341
+ if (propertyGroupType === 'AND') {
2342
+ // if negated property, do the inverse
2343
+ if (!matches && !negation) {
2344
+ return false;
2345
+ }
2346
+
2347
+ if (matches && negation) {
2348
+ return false;
2349
+ }
2350
+ } else {
2351
+ // OR group
2352
+ if (matches && !negation) {
2353
+ return true;
2354
+ }
2355
+
2356
+ if (!matches && negation) {
2357
+ return true;
2358
+ }
2359
+ }
2360
+ } catch (err) {
2361
+ if (err instanceof InconclusiveMatchError) {
2362
+ console.debug("Failed to compute property ".concat(prop, " locally: ").concat(err));
2363
+ errorMatchingLocally = true;
2364
+ } else {
2365
+ throw err;
2366
+ }
2367
+ }
2368
+ }
2369
+
2370
+ if (errorMatchingLocally) {
2371
+ throw new InconclusiveMatchError("can't match cohort without a given cohort property value");
2372
+ } // if we get here, all matched in AND case, or none matched in OR case
2373
+
2374
+
2375
+ return propertyGroupType === 'AND';
2376
+ }
2377
+ }
2378
+
2238
2379
  function isValidRegex(regex) {
2239
2380
  try {
2240
2381
  new RegExp(regex);
@@ -2330,6 +2471,18 @@ function (_super) {
2330
2471
  return _super.prototype.optOut.call(this);
2331
2472
  };
2332
2473
 
2474
+ PostHog.prototype.debug = function (enabled) {
2475
+ var _a;
2476
+
2477
+ if (enabled === void 0) {
2478
+ enabled = true;
2479
+ }
2480
+
2481
+ _super.prototype.debug.call(this, enabled);
2482
+
2483
+ (_a = this.featureFlagsPoller) === null || _a === void 0 ? void 0 : _a.debug(enabled);
2484
+ };
2485
+
2333
2486
  PostHog.prototype.capture = function (_a) {
2334
2487
  var _this = this;
2335
2488