posthog-node 5.8.8 → 5.9.1

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.
Files changed (99) hide show
  1. package/dist/{index.d.ts → client.d.ts} +7 -378
  2. package/dist/client.d.ts.map +1 -0
  3. package/dist/client.js +480 -0
  4. package/dist/client.mjs +436 -0
  5. package/dist/entrypoints/index.edge.d.ts +6 -0
  6. package/dist/entrypoints/index.edge.d.ts.map +1 -0
  7. package/dist/entrypoints/index.edge.js +96 -0
  8. package/dist/entrypoints/index.edge.mjs +19 -0
  9. package/dist/entrypoints/index.node.d.ts +6 -0
  10. package/dist/entrypoints/index.node.d.ts.map +1 -0
  11. package/dist/entrypoints/index.node.js +107 -0
  12. package/dist/entrypoints/index.node.mjs +24 -0
  13. package/dist/exports.d.ts +4 -0
  14. package/dist/exports.d.ts.map +1 -0
  15. package/dist/exports.js +78 -0
  16. package/dist/exports.mjs +3 -0
  17. package/dist/extensions/error-tracking/autocapture.d.ts +4 -0
  18. package/dist/extensions/error-tracking/autocapture.d.ts.map +1 -0
  19. package/dist/extensions/error-tracking/autocapture.js +68 -0
  20. package/dist/extensions/error-tracking/autocapture.mjs +31 -0
  21. package/dist/extensions/error-tracking/index.d.ts +19 -0
  22. package/dist/extensions/error-tracking/index.d.ts.map +1 -0
  23. package/dist/extensions/error-tracking/index.js +97 -0
  24. package/dist/extensions/error-tracking/index.mjs +63 -0
  25. package/dist/extensions/error-tracking/modifiers/context-lines.node.d.ts +5 -0
  26. package/dist/extensions/error-tracking/modifiers/context-lines.node.d.ts.map +1 -0
  27. package/dist/extensions/error-tracking/modifiers/context-lines.node.js +227 -0
  28. package/dist/extensions/error-tracking/modifiers/context-lines.node.mjs +187 -0
  29. package/dist/extensions/error-tracking/modifiers/module.node.d.ts +3 -0
  30. package/dist/extensions/error-tracking/modifiers/module.node.d.ts.map +1 -0
  31. package/dist/extensions/error-tracking/modifiers/module.node.js +64 -0
  32. package/dist/extensions/error-tracking/modifiers/module.node.mjs +30 -0
  33. package/dist/extensions/express.d.ts +17 -0
  34. package/dist/extensions/express.d.ts.map +1 -0
  35. package/dist/extensions/express.js +61 -0
  36. package/dist/extensions/express.mjs +17 -0
  37. package/dist/extensions/feature-flags/crypto-helpers.d.ts +3 -0
  38. package/dist/extensions/feature-flags/crypto-helpers.d.ts.map +1 -0
  39. package/dist/extensions/feature-flags/crypto-helpers.js +77 -0
  40. package/dist/extensions/feature-flags/crypto-helpers.mjs +22 -0
  41. package/dist/extensions/feature-flags/crypto.d.ts +2 -0
  42. package/dist/extensions/feature-flags/crypto.d.ts.map +1 -0
  43. package/dist/extensions/feature-flags/crypto.js +47 -0
  44. package/dist/extensions/feature-flags/crypto.mjs +13 -0
  45. package/dist/extensions/feature-flags/feature-flags.d.ts +89 -0
  46. package/dist/extensions/feature-flags/feature-flags.d.ts.map +1 -0
  47. package/dist/extensions/feature-flags/feature-flags.js +529 -0
  48. package/dist/extensions/feature-flags/feature-flags.mjs +483 -0
  49. package/dist/extensions/feature-flags/lazy.d.ts +24 -0
  50. package/dist/extensions/feature-flags/lazy.d.ts.map +1 -0
  51. package/dist/extensions/feature-flags/lazy.js +60 -0
  52. package/dist/extensions/feature-flags/lazy.mjs +26 -0
  53. package/dist/extensions/sentry-integration.d.ts +54 -0
  54. package/dist/extensions/sentry-integration.d.ts.map +1 -0
  55. package/dist/extensions/sentry-integration.js +113 -0
  56. package/dist/extensions/sentry-integration.mjs +73 -0
  57. package/dist/storage-memory.d.ts +7 -0
  58. package/dist/storage-memory.d.ts.map +1 -0
  59. package/dist/storage-memory.js +46 -0
  60. package/dist/storage-memory.mjs +12 -0
  61. package/dist/types.d.ts +253 -0
  62. package/dist/types.d.ts.map +1 -0
  63. package/dist/types.js +18 -0
  64. package/dist/types.mjs +0 -0
  65. package/dist/utils/logger.d.ts +3 -0
  66. package/dist/utils/logger.d.ts.map +1 -0
  67. package/dist/utils/logger.js +63 -0
  68. package/dist/utils/logger.mjs +29 -0
  69. package/dist/version.d.ts +2 -0
  70. package/dist/version.d.ts.map +1 -0
  71. package/dist/version.js +36 -0
  72. package/dist/version.mjs +2 -0
  73. package/package.json +32 -31
  74. package/src/client.ts +1532 -0
  75. package/src/entrypoints/index.edge.ts +22 -0
  76. package/src/entrypoints/index.node.ts +26 -0
  77. package/src/exports.ts +3 -0
  78. package/src/extensions/error-tracking/autocapture.ts +67 -0
  79. package/src/extensions/error-tracking/index.ts +104 -0
  80. package/src/extensions/error-tracking/modifiers/context-lines.node.ts +404 -0
  81. package/src/extensions/error-tracking/modifiers/module.node.ts +68 -0
  82. package/src/extensions/express.ts +40 -0
  83. package/src/extensions/feature-flags/crypto-helpers.ts +36 -0
  84. package/src/extensions/feature-flags/crypto.ts +22 -0
  85. package/src/extensions/feature-flags/feature-flags.ts +1003 -0
  86. package/src/extensions/feature-flags/lazy.ts +55 -0
  87. package/src/extensions/sentry-integration.ts +216 -0
  88. package/src/storage-memory.ts +13 -0
  89. package/src/types.ts +294 -0
  90. package/src/utils/logger.ts +39 -0
  91. package/src/version.ts +1 -0
  92. package/dist/edge/index.cjs +0 -3150
  93. package/dist/edge/index.cjs.map +0 -1
  94. package/dist/edge/index.mjs +0 -3144
  95. package/dist/edge/index.mjs.map +0 -1
  96. package/dist/node/index.cjs +0 -3556
  97. package/dist/node/index.cjs.map +0 -1
  98. package/dist/node/index.mjs +0 -3550
  99. package/dist/node/index.mjs.map +0 -1
@@ -0,0 +1,483 @@
1
+ import { safeSetTimeout } from "@posthog/core";
2
+ import { hashSHA1 } from "./crypto.mjs";
3
+ const SIXTY_SECONDS = 60000;
4
+ const LONG_SCALE = 0xfffffffffffffff;
5
+ const NULL_VALUES_ALLOWED_OPERATORS = [
6
+ 'is_not'
7
+ ];
8
+ class ClientError extends Error {
9
+ constructor(message){
10
+ super();
11
+ Error.captureStackTrace(this, this.constructor);
12
+ this.name = 'ClientError';
13
+ this.message = message;
14
+ Object.setPrototypeOf(this, ClientError.prototype);
15
+ }
16
+ }
17
+ class InconclusiveMatchError extends Error {
18
+ constructor(message){
19
+ super(message);
20
+ this.name = this.constructor.name;
21
+ Error.captureStackTrace(this, this.constructor);
22
+ Object.setPrototypeOf(this, InconclusiveMatchError.prototype);
23
+ }
24
+ }
25
+ class FeatureFlagsPoller {
26
+ constructor({ pollingInterval, personalApiKey, projectApiKey, timeout, host, customHeaders, ...options }){
27
+ this.debugMode = false;
28
+ this.shouldBeginExponentialBackoff = false;
29
+ this.backOffCount = 0;
30
+ this.pollingInterval = pollingInterval;
31
+ this.personalApiKey = personalApiKey;
32
+ this.featureFlags = [];
33
+ this.featureFlagsByKey = {};
34
+ this.groupTypeMapping = {};
35
+ this.cohorts = {};
36
+ this.loadedSuccessfullyOnce = false;
37
+ this.timeout = timeout;
38
+ this.projectApiKey = projectApiKey;
39
+ this.host = host;
40
+ this.poller = void 0;
41
+ this.fetch = options.fetch || fetch;
42
+ this.onError = options.onError;
43
+ this.customHeaders = customHeaders;
44
+ this.onLoad = options.onLoad;
45
+ this.loadFeatureFlags();
46
+ }
47
+ debug(enabled = true) {
48
+ this.debugMode = enabled;
49
+ }
50
+ logMsgIfDebug(fn) {
51
+ if (this.debugMode) fn();
52
+ }
53
+ async getFeatureFlag(key, distinctId, groups = {}, personProperties = {}, groupProperties = {}) {
54
+ await this.loadFeatureFlags();
55
+ let response;
56
+ let featureFlag;
57
+ if (!this.loadedSuccessfullyOnce) return response;
58
+ featureFlag = this.featureFlagsByKey[key];
59
+ if (void 0 !== featureFlag) try {
60
+ const result = await this.computeFlagAndPayloadLocally(featureFlag, distinctId, groups, personProperties, groupProperties);
61
+ response = result.value;
62
+ this.logMsgIfDebug(()=>console.debug(`Successfully computed flag locally: ${key} -> ${response}`));
63
+ } catch (e) {
64
+ if (e instanceof InconclusiveMatchError) this.logMsgIfDebug(()=>console.debug(`InconclusiveMatchError when computing flag locally: ${key}: ${e}`));
65
+ else if (e instanceof Error) this.onError?.(new Error(`Error computing flag locally: ${key}: ${e}`));
66
+ }
67
+ return response;
68
+ }
69
+ async getAllFlagsAndPayloads(distinctId, groups = {}, personProperties = {}, groupProperties = {}, flagKeysToExplicitlyEvaluate) {
70
+ await this.loadFeatureFlags();
71
+ const response = {};
72
+ const payloads = {};
73
+ let fallbackToFlags = 0 == this.featureFlags.length;
74
+ const flagsToEvaluate = flagKeysToExplicitlyEvaluate ? flagKeysToExplicitlyEvaluate.map((key)=>this.featureFlagsByKey[key]).filter(Boolean) : this.featureFlags;
75
+ const sharedEvaluationCache = {};
76
+ await Promise.all(flagsToEvaluate.map(async (flag)=>{
77
+ try {
78
+ const { value: matchValue, payload: matchPayload } = await this.computeFlagAndPayloadLocally(flag, distinctId, groups, personProperties, groupProperties, void 0, sharedEvaluationCache);
79
+ response[flag.key] = matchValue;
80
+ if (matchPayload) payloads[flag.key] = matchPayload;
81
+ } catch (e) {
82
+ if (e instanceof InconclusiveMatchError) this.logMsgIfDebug(()=>console.debug(`InconclusiveMatchError when computing flag locally: ${flag.key}: ${e}`));
83
+ else if (e instanceof Error) this.onError?.(new Error(`Error computing flag locally: ${flag.key}: ${e}`));
84
+ fallbackToFlags = true;
85
+ }
86
+ }));
87
+ return {
88
+ response,
89
+ payloads,
90
+ fallbackToFlags
91
+ };
92
+ }
93
+ async computeFlagAndPayloadLocally(flag, distinctId, groups = {}, personProperties = {}, groupProperties = {}, matchValue, evaluationCache, skipLoadCheck = false) {
94
+ if (!skipLoadCheck) await this.loadFeatureFlags();
95
+ if (!this.loadedSuccessfullyOnce) return {
96
+ value: false,
97
+ payload: null
98
+ };
99
+ let flagValue;
100
+ flagValue = void 0 !== matchValue ? matchValue : await this.computeFlagValueLocally(flag, distinctId, groups, personProperties, groupProperties, evaluationCache);
101
+ const payload = this.getFeatureFlagPayload(flag.key, flagValue);
102
+ return {
103
+ value: flagValue,
104
+ payload
105
+ };
106
+ }
107
+ async computeFlagValueLocally(flag, distinctId, groups = {}, personProperties = {}, groupProperties = {}, evaluationCache = {}) {
108
+ if (flag.ensure_experience_continuity) throw new InconclusiveMatchError('Flag has experience continuity enabled');
109
+ if (!flag.active) return false;
110
+ const flagFilters = flag.filters || {};
111
+ const aggregation_group_type_index = flagFilters.aggregation_group_type_index;
112
+ if (void 0 == aggregation_group_type_index) return await this.matchFeatureFlagProperties(flag, distinctId, personProperties, evaluationCache);
113
+ {
114
+ const groupName = this.groupTypeMapping[String(aggregation_group_type_index)];
115
+ if (!groupName) {
116
+ this.logMsgIfDebug(()=>console.warn(`[FEATURE FLAGS] Unknown group type index ${aggregation_group_type_index} for feature flag ${flag.key}`));
117
+ throw new InconclusiveMatchError('Flag has unknown group type index');
118
+ }
119
+ if (!(groupName in groups)) {
120
+ this.logMsgIfDebug(()=>console.warn(`[FEATURE FLAGS] Can't compute group feature flag: ${flag.key} without group names passed in`));
121
+ return false;
122
+ }
123
+ const focusedGroupProperties = groupProperties[groupName];
124
+ return await this.matchFeatureFlagProperties(flag, groups[groupName], focusedGroupProperties, evaluationCache);
125
+ }
126
+ }
127
+ getFeatureFlagPayload(key, flagValue) {
128
+ let payload = null;
129
+ if (false !== flagValue && null != flagValue) {
130
+ if ('boolean' == typeof flagValue) payload = this.featureFlagsByKey?.[key]?.filters?.payloads?.[flagValue.toString()] || null;
131
+ else if ('string' == typeof flagValue) payload = this.featureFlagsByKey?.[key]?.filters?.payloads?.[flagValue] || null;
132
+ if (null != payload) {
133
+ if ('object' == typeof payload) return payload;
134
+ if ('string' == typeof payload) try {
135
+ return JSON.parse(payload);
136
+ } catch {}
137
+ return payload;
138
+ }
139
+ }
140
+ return null;
141
+ }
142
+ async evaluateFlagDependency(property, distinctId, properties, evaluationCache) {
143
+ const targetFlagKey = property.key;
144
+ if (!this.featureFlagsByKey) throw new InconclusiveMatchError('Feature flags not available for dependency evaluation');
145
+ if (!('dependency_chain' in property)) throw new InconclusiveMatchError(`Flag dependency property for '${targetFlagKey}' is missing required 'dependency_chain' field`);
146
+ const dependencyChain = property.dependency_chain;
147
+ if (!Array.isArray(dependencyChain)) throw new InconclusiveMatchError(`Flag dependency property for '${targetFlagKey}' has an invalid 'dependency_chain' (expected array, got ${typeof dependencyChain})`);
148
+ if (0 === dependencyChain.length) throw new InconclusiveMatchError(`Circular dependency detected for flag '${targetFlagKey}' (empty dependency chain)`);
149
+ for (const depFlagKey of dependencyChain){
150
+ if (!(depFlagKey in evaluationCache)) {
151
+ const depFlag = this.featureFlagsByKey[depFlagKey];
152
+ if (depFlag) if (depFlag.active) try {
153
+ const depResult = await this.matchFeatureFlagProperties(depFlag, distinctId, properties, evaluationCache);
154
+ evaluationCache[depFlagKey] = depResult;
155
+ } catch (error) {
156
+ throw new InconclusiveMatchError(`Error evaluating flag dependency '${depFlagKey}' for flag '${targetFlagKey}': ${error}`);
157
+ }
158
+ else evaluationCache[depFlagKey] = false;
159
+ else throw new InconclusiveMatchError(`Missing flag dependency '${depFlagKey}' for flag '${targetFlagKey}'`);
160
+ }
161
+ const cachedResult = evaluationCache[depFlagKey];
162
+ if (null == cachedResult) throw new InconclusiveMatchError(`Dependency '${depFlagKey}' could not be evaluated`);
163
+ }
164
+ const targetFlagValue = evaluationCache[targetFlagKey];
165
+ return this.flagEvaluatesToExpectedValue(property.value, targetFlagValue);
166
+ }
167
+ flagEvaluatesToExpectedValue(expectedValue, flagValue) {
168
+ if ('boolean' == typeof expectedValue) return expectedValue === flagValue || 'string' == typeof flagValue && '' !== flagValue && true === expectedValue;
169
+ if ('string' == typeof expectedValue) return flagValue === expectedValue;
170
+ return false;
171
+ }
172
+ async matchFeatureFlagProperties(flag, distinctId, properties, evaluationCache = {}) {
173
+ const flagFilters = flag.filters || {};
174
+ const flagConditions = flagFilters.groups || [];
175
+ let isInconclusive = false;
176
+ let result;
177
+ for (const condition of flagConditions)try {
178
+ if (await this.isConditionMatch(flag, distinctId, condition, properties, evaluationCache)) {
179
+ const variantOverride = condition.variant;
180
+ const flagVariants = flagFilters.multivariate?.variants || [];
181
+ result = variantOverride && flagVariants.some((variant)=>variant.key === variantOverride) ? variantOverride : await this.getMatchingVariant(flag, distinctId) || true;
182
+ break;
183
+ }
184
+ } catch (e) {
185
+ if (e instanceof InconclusiveMatchError) isInconclusive = true;
186
+ else throw e;
187
+ }
188
+ if (void 0 !== result) return result;
189
+ if (isInconclusive) throw new InconclusiveMatchError("Can't determine if feature flag is enabled or not with given properties");
190
+ return false;
191
+ }
192
+ async isConditionMatch(flag, distinctId, condition, properties, evaluationCache = {}) {
193
+ const rolloutPercentage = condition.rollout_percentage;
194
+ const warnFunction = (msg)=>{
195
+ this.logMsgIfDebug(()=>console.warn(msg));
196
+ };
197
+ if ((condition.properties || []).length > 0) {
198
+ for (const prop of condition.properties){
199
+ const propertyType = prop.type;
200
+ let matches = false;
201
+ matches = 'cohort' === propertyType ? matchCohort(prop, properties, this.cohorts, this.debugMode) : 'flag' === propertyType ? await this.evaluateFlagDependency(prop, distinctId, properties, evaluationCache) : matchProperty(prop, properties, warnFunction);
202
+ if (!matches) return false;
203
+ }
204
+ if (void 0 == rolloutPercentage) return true;
205
+ }
206
+ if (void 0 != rolloutPercentage && await _hash(flag.key, distinctId) > rolloutPercentage / 100.0) return false;
207
+ return true;
208
+ }
209
+ async getMatchingVariant(flag, distinctId) {
210
+ const hashValue = await _hash(flag.key, distinctId, 'variant');
211
+ const matchingVariant = this.variantLookupTable(flag).find((variant)=>hashValue >= variant.valueMin && hashValue < variant.valueMax);
212
+ if (matchingVariant) return matchingVariant.key;
213
+ }
214
+ variantLookupTable(flag) {
215
+ const lookupTable = [];
216
+ let valueMin = 0;
217
+ let valueMax = 0;
218
+ const flagFilters = flag.filters || {};
219
+ const multivariates = flagFilters.multivariate?.variants || [];
220
+ multivariates.forEach((variant)=>{
221
+ valueMax = valueMin + variant.rollout_percentage / 100.0;
222
+ lookupTable.push({
223
+ valueMin,
224
+ valueMax,
225
+ key: variant.key
226
+ });
227
+ valueMin = valueMax;
228
+ });
229
+ return lookupTable;
230
+ }
231
+ async loadFeatureFlags(forceReload = false) {
232
+ if (!this.loadedSuccessfullyOnce || forceReload) await this._loadFeatureFlags();
233
+ }
234
+ isLocalEvaluationReady() {
235
+ return (this.loadedSuccessfullyOnce ?? false) && (this.featureFlags?.length ?? 0) > 0;
236
+ }
237
+ getPollingInterval() {
238
+ if (!this.shouldBeginExponentialBackoff) return this.pollingInterval;
239
+ return Math.min(SIXTY_SECONDS, this.pollingInterval * 2 ** this.backOffCount);
240
+ }
241
+ async _loadFeatureFlags() {
242
+ if (this.poller) {
243
+ clearTimeout(this.poller);
244
+ this.poller = void 0;
245
+ }
246
+ this.poller = setTimeout(()=>this._loadFeatureFlags(), this.getPollingInterval());
247
+ try {
248
+ const res = await this._requestFeatureFlagDefinitions();
249
+ if (!res) return;
250
+ switch(res.status){
251
+ case 401:
252
+ this.shouldBeginExponentialBackoff = true;
253
+ this.backOffCount += 1;
254
+ throw new ClientError(`Your project key or personal API key is invalid. Setting next polling interval to ${this.getPollingInterval()}ms. More information: https://posthog.com/docs/api#rate-limiting`);
255
+ case 402:
256
+ console.warn('[FEATURE FLAGS] Feature flags quota limit exceeded - unsetting all local flags. Learn more about billing limits at https://posthog.com/docs/billing/limits-alerts');
257
+ this.featureFlags = [];
258
+ this.featureFlagsByKey = {};
259
+ this.groupTypeMapping = {};
260
+ this.cohorts = {};
261
+ return;
262
+ case 403:
263
+ this.shouldBeginExponentialBackoff = true;
264
+ this.backOffCount += 1;
265
+ throw new ClientError(`Your personal API key does not have permission to fetch feature flag definitions for local evaluation. Setting next polling interval to ${this.getPollingInterval()}ms. Are you sure you're using the correct personal and Project API key pair? More information: https://posthog.com/docs/api/overview`);
266
+ case 429:
267
+ this.shouldBeginExponentialBackoff = true;
268
+ this.backOffCount += 1;
269
+ throw new ClientError(`You are being rate limited. Setting next polling interval to ${this.getPollingInterval()}ms. More information: https://posthog.com/docs/api#rate-limiting`);
270
+ case 200:
271
+ {
272
+ const responseJson = await res.json() ?? {};
273
+ if (!('flags' in responseJson)) return void this.onError?.(new Error(`Invalid response when getting feature flags: ${JSON.stringify(responseJson)}`));
274
+ this.featureFlags = responseJson.flags ?? [];
275
+ this.featureFlagsByKey = this.featureFlags.reduce((acc, curr)=>(acc[curr.key] = curr, acc), {});
276
+ this.groupTypeMapping = responseJson.group_type_mapping || {};
277
+ this.cohorts = responseJson.cohorts || {};
278
+ this.loadedSuccessfullyOnce = true;
279
+ this.shouldBeginExponentialBackoff = false;
280
+ this.backOffCount = 0;
281
+ this.onLoad?.(this.featureFlags.length);
282
+ break;
283
+ }
284
+ default:
285
+ return;
286
+ }
287
+ } catch (err) {
288
+ if (err instanceof ClientError) this.onError?.(err);
289
+ }
290
+ }
291
+ getPersonalApiKeyRequestOptions(method = 'GET') {
292
+ return {
293
+ method,
294
+ headers: {
295
+ ...this.customHeaders,
296
+ 'Content-Type': 'application/json',
297
+ Authorization: `Bearer ${this.personalApiKey}`
298
+ }
299
+ };
300
+ }
301
+ async _requestFeatureFlagDefinitions() {
302
+ const url = `${this.host}/api/feature_flag/local_evaluation?token=${this.projectApiKey}&send_cohorts`;
303
+ const options = this.getPersonalApiKeyRequestOptions();
304
+ let abortTimeout = null;
305
+ if (this.timeout && 'number' == typeof this.timeout) {
306
+ const controller = new AbortController();
307
+ abortTimeout = safeSetTimeout(()=>{
308
+ controller.abort();
309
+ }, this.timeout);
310
+ options.signal = controller.signal;
311
+ }
312
+ try {
313
+ return await this.fetch(url, options);
314
+ } finally{
315
+ clearTimeout(abortTimeout);
316
+ }
317
+ }
318
+ stopPoller() {
319
+ clearTimeout(this.poller);
320
+ }
321
+ }
322
+ async function _hash(key, distinctId, salt = '') {
323
+ const hashString = await hashSHA1(`${key}.${distinctId}${salt}`);
324
+ return parseInt(hashString.slice(0, 15), 16) / LONG_SCALE;
325
+ }
326
+ function matchProperty(property, propertyValues, warnFunction) {
327
+ const key = property.key;
328
+ const value = property.value;
329
+ const operator = property.operator || 'exact';
330
+ if (key in propertyValues) {
331
+ if ('is_not_set' === operator) throw new InconclusiveMatchError("Operator is_not_set is not supported");
332
+ } else throw new InconclusiveMatchError(`Property ${key} not found in propertyValues`);
333
+ const overrideValue = propertyValues[key];
334
+ if (null == overrideValue && !NULL_VALUES_ALLOWED_OPERATORS.includes(operator)) {
335
+ if (warnFunction) warnFunction(`Property ${key} cannot have a value of null/undefined with the ${operator} operator`);
336
+ return false;
337
+ }
338
+ function computeExactMatch(value, overrideValue) {
339
+ if (Array.isArray(value)) return value.map((val)=>String(val).toLowerCase()).includes(String(overrideValue).toLowerCase());
340
+ return String(value).toLowerCase() === String(overrideValue).toLowerCase();
341
+ }
342
+ function compare(lhs, rhs, operator) {
343
+ if ('gt' === operator) return lhs > rhs;
344
+ if ('gte' === operator) return lhs >= rhs;
345
+ if ('lt' === operator) return lhs < rhs;
346
+ if ('lte' === operator) return lhs <= rhs;
347
+ throw new Error(`Invalid operator: ${operator}`);
348
+ }
349
+ switch(operator){
350
+ case 'exact':
351
+ return computeExactMatch(value, overrideValue);
352
+ case 'is_not':
353
+ return !computeExactMatch(value, overrideValue);
354
+ case 'is_set':
355
+ return key in propertyValues;
356
+ case 'icontains':
357
+ return String(overrideValue).toLowerCase().includes(String(value).toLowerCase());
358
+ case 'not_icontains':
359
+ return !String(overrideValue).toLowerCase().includes(String(value).toLowerCase());
360
+ case 'regex':
361
+ return isValidRegex(String(value)) && null !== String(overrideValue).match(String(value));
362
+ case 'not_regex':
363
+ return isValidRegex(String(value)) && null === String(overrideValue).match(String(value));
364
+ case 'gt':
365
+ case 'gte':
366
+ case 'lt':
367
+ case 'lte':
368
+ {
369
+ let parsedValue = 'number' == typeof value ? value : null;
370
+ if ('string' == typeof value) try {
371
+ parsedValue = parseFloat(value);
372
+ } catch (err) {}
373
+ if (null == parsedValue || null == overrideValue) return compare(String(overrideValue), String(value), operator);
374
+ if ('string' == typeof overrideValue) return compare(overrideValue, String(value), operator);
375
+ return compare(overrideValue, parsedValue, operator);
376
+ }
377
+ case 'is_date_after':
378
+ case 'is_date_before':
379
+ {
380
+ if ('boolean' == typeof value) throw new InconclusiveMatchError("Date operations cannot be performed on boolean values");
381
+ let parsedDate = relativeDateParseForFeatureFlagMatching(String(value));
382
+ if (null == parsedDate) parsedDate = convertToDateTime(value);
383
+ if (null == parsedDate) throw new InconclusiveMatchError(`Invalid date: ${value}`);
384
+ const overrideDate = convertToDateTime(overrideValue);
385
+ if ([
386
+ 'is_date_before'
387
+ ].includes(operator)) return overrideDate < parsedDate;
388
+ return overrideDate > parsedDate;
389
+ }
390
+ default:
391
+ throw new InconclusiveMatchError(`Unknown operator: ${operator}`);
392
+ }
393
+ }
394
+ function matchCohort(property, propertyValues, cohortProperties, debugMode = false) {
395
+ const cohortId = String(property.value);
396
+ if (!(cohortId in cohortProperties)) throw new InconclusiveMatchError("can't match cohort without a given cohort property value");
397
+ const propertyGroup = cohortProperties[cohortId];
398
+ return matchPropertyGroup(propertyGroup, propertyValues, cohortProperties, debugMode);
399
+ }
400
+ function matchPropertyGroup(propertyGroup, propertyValues, cohortProperties, debugMode = false) {
401
+ if (!propertyGroup) return true;
402
+ const propertyGroupType = propertyGroup.type;
403
+ const properties = propertyGroup.values;
404
+ if (!properties || 0 === properties.length) return true;
405
+ let errorMatchingLocally = false;
406
+ if ('values' in properties[0]) {
407
+ for (const prop of properties)try {
408
+ const matches = matchPropertyGroup(prop, propertyValues, cohortProperties, debugMode);
409
+ if ('AND' === propertyGroupType) {
410
+ if (!matches) return false;
411
+ } else if (matches) return true;
412
+ } catch (err) {
413
+ if (err instanceof InconclusiveMatchError) {
414
+ if (debugMode) console.debug(`Failed to compute property ${prop} locally: ${err}`);
415
+ errorMatchingLocally = true;
416
+ } else throw err;
417
+ }
418
+ if (errorMatchingLocally) throw new InconclusiveMatchError("Can't match cohort without a given cohort property value");
419
+ return 'AND' === propertyGroupType;
420
+ }
421
+ for (const prop of properties)try {
422
+ let matches;
423
+ if ('cohort' === prop.type) matches = matchCohort(prop, propertyValues, cohortProperties, debugMode);
424
+ else if ('flag' === prop.type) {
425
+ if (debugMode) console.warn(`[FEATURE FLAGS] Flag dependency filters are not supported in local evaluation. Skipping condition with dependency on flag '${prop.key || 'unknown'}'`);
426
+ continue;
427
+ } else matches = matchProperty(prop, propertyValues);
428
+ const negation = prop.negation || false;
429
+ if ('AND' === propertyGroupType) {
430
+ if (!matches && !negation) return false;
431
+ if (matches && negation) return false;
432
+ } else {
433
+ if (matches && !negation) return true;
434
+ if (!matches && negation) return true;
435
+ }
436
+ } catch (err) {
437
+ if (err instanceof InconclusiveMatchError) {
438
+ if (debugMode) console.debug(`Failed to compute property ${prop} locally: ${err}`);
439
+ errorMatchingLocally = true;
440
+ } else throw err;
441
+ }
442
+ if (errorMatchingLocally) throw new InconclusiveMatchError("can't match cohort without a given cohort property value");
443
+ return 'AND' === propertyGroupType;
444
+ }
445
+ function isValidRegex(regex) {
446
+ try {
447
+ new RegExp(regex);
448
+ return true;
449
+ } catch (err) {
450
+ return false;
451
+ }
452
+ }
453
+ function convertToDateTime(value) {
454
+ if (value instanceof Date) return value;
455
+ if ('string' == typeof value || 'number' == typeof value) {
456
+ const date = new Date(value);
457
+ if (!isNaN(date.valueOf())) return date;
458
+ throw new InconclusiveMatchError(`${value} is in an invalid date format`);
459
+ }
460
+ throw new InconclusiveMatchError(`The date provided ${value} must be a string, number, or date object`);
461
+ }
462
+ function relativeDateParseForFeatureFlagMatching(value) {
463
+ const regex = /^-?(?<number>[0-9]+)(?<interval>[a-z])$/;
464
+ const match = value.match(regex);
465
+ const parsedDt = new Date(new Date().toISOString());
466
+ if (!match) return null;
467
+ {
468
+ if (!match.groups) return null;
469
+ const number = parseInt(match.groups['number']);
470
+ if (number >= 10000) return null;
471
+ const interval = match.groups['interval'];
472
+ if ('h' == interval) parsedDt.setUTCHours(parsedDt.getUTCHours() - number);
473
+ else if ('d' == interval) parsedDt.setUTCDate(parsedDt.getUTCDate() - number);
474
+ else if ('w' == interval) parsedDt.setUTCDate(parsedDt.getUTCDate() - 7 * number);
475
+ else if ('m' == interval) parsedDt.setUTCMonth(parsedDt.getUTCMonth() - number);
476
+ else {
477
+ if ('y' != interval) return null;
478
+ parsedDt.setUTCFullYear(parsedDt.getUTCFullYear() - number);
479
+ }
480
+ return parsedDt;
481
+ }
482
+ }
483
+ export { ClientError, FeatureFlagsPoller, InconclusiveMatchError, matchProperty, relativeDateParseForFeatureFlagMatching };
@@ -0,0 +1,24 @@
1
+ /**
2
+ * A lazy value that is only computed when needed. Inspired by C#'s Lazy<T> class.
3
+ */
4
+ export declare class Lazy<T> {
5
+ private value;
6
+ private factory;
7
+ private initializationPromise;
8
+ constructor(factory: () => Promise<T>);
9
+ /**
10
+ * Gets the value, initializing it if necessary.
11
+ * Multiple concurrent calls will share the same initialization promise.
12
+ */
13
+ getValue(): Promise<T>;
14
+ /**
15
+ * Returns true if the value has been initialized.
16
+ */
17
+ isInitialized(): boolean;
18
+ /**
19
+ * Returns a promise that resolves when the value is initialized.
20
+ * If already initialized, resolves immediately.
21
+ */
22
+ waitForInitialization(): Promise<void>;
23
+ }
24
+ //# sourceMappingURL=lazy.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lazy.d.ts","sourceRoot":"","sources":["../../../src/extensions/feature-flags/lazy.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,qBAAa,IAAI,CAAC,CAAC;IACjB,OAAO,CAAC,KAAK,CAAe;IAC5B,OAAO,CAAC,OAAO,CAAkB;IACjC,OAAO,CAAC,qBAAqB,CAAwB;gBAEzC,OAAO,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC;IAIrC;;;OAGG;IACG,QAAQ,IAAI,OAAO,CAAC,CAAC,CAAC;IAqB5B;;OAEG;IACH,aAAa,IAAI,OAAO;IAIxB;;;OAGG;IACG,qBAAqB,IAAI,OAAO,CAAC,IAAI,CAAC;CAM7C"}
@@ -0,0 +1,60 @@
1
+ "use strict";
2
+ var __webpack_require__ = {};
3
+ (()=>{
4
+ __webpack_require__.d = (exports1, definition)=>{
5
+ for(var key in definition)if (__webpack_require__.o(definition, key) && !__webpack_require__.o(exports1, key)) Object.defineProperty(exports1, key, {
6
+ enumerable: true,
7
+ get: definition[key]
8
+ });
9
+ };
10
+ })();
11
+ (()=>{
12
+ __webpack_require__.o = (obj, prop)=>Object.prototype.hasOwnProperty.call(obj, prop);
13
+ })();
14
+ (()=>{
15
+ __webpack_require__.r = (exports1)=>{
16
+ if ('undefined' != typeof Symbol && Symbol.toStringTag) Object.defineProperty(exports1, Symbol.toStringTag, {
17
+ value: 'Module'
18
+ });
19
+ Object.defineProperty(exports1, '__esModule', {
20
+ value: true
21
+ });
22
+ };
23
+ })();
24
+ var __webpack_exports__ = {};
25
+ __webpack_require__.r(__webpack_exports__);
26
+ __webpack_require__.d(__webpack_exports__, {
27
+ Lazy: ()=>Lazy
28
+ });
29
+ class Lazy {
30
+ constructor(factory){
31
+ this.factory = factory;
32
+ }
33
+ async getValue() {
34
+ if (void 0 !== this.value) return this.value;
35
+ if (void 0 === this.initializationPromise) this.initializationPromise = (async ()=>{
36
+ try {
37
+ const result = await this.factory();
38
+ this.value = result;
39
+ return result;
40
+ } finally{
41
+ this.initializationPromise = void 0;
42
+ }
43
+ })();
44
+ return this.initializationPromise;
45
+ }
46
+ isInitialized() {
47
+ return void 0 !== this.value;
48
+ }
49
+ async waitForInitialization() {
50
+ if (this.isInitialized()) return;
51
+ await this.getValue();
52
+ }
53
+ }
54
+ exports.Lazy = __webpack_exports__.Lazy;
55
+ for(var __webpack_i__ in __webpack_exports__)if (-1 === [
56
+ "Lazy"
57
+ ].indexOf(__webpack_i__)) exports[__webpack_i__] = __webpack_exports__[__webpack_i__];
58
+ Object.defineProperty(exports, '__esModule', {
59
+ value: true
60
+ });
@@ -0,0 +1,26 @@
1
+ class Lazy {
2
+ constructor(factory){
3
+ this.factory = factory;
4
+ }
5
+ async getValue() {
6
+ if (void 0 !== this.value) return this.value;
7
+ if (void 0 === this.initializationPromise) this.initializationPromise = (async ()=>{
8
+ try {
9
+ const result = await this.factory();
10
+ this.value = result;
11
+ return result;
12
+ } finally{
13
+ this.initializationPromise = void 0;
14
+ }
15
+ })();
16
+ return this.initializationPromise;
17
+ }
18
+ isInitialized() {
19
+ return void 0 !== this.value;
20
+ }
21
+ async waitForInitialization() {
22
+ if (this.isInitialized()) return;
23
+ await this.getValue();
24
+ }
25
+ }
26
+ export { Lazy };
@@ -0,0 +1,54 @@
1
+ /**
2
+ * @file Adapted from [posthog-js](https://github.com/PostHog/posthog-js/blob/8157df935a4d0e71d2fefef7127aa85ee51c82d1/src/extensions/sentry-integration.ts) with modifications for the Node SDK.
3
+ */
4
+ /**
5
+ * Integrate Sentry with PostHog. This will add a direct link to the person in Sentry, and an $exception event in PostHog.
6
+ *
7
+ * ### Usage
8
+ *
9
+ * Sentry.init({
10
+ * dsn: 'https://example',
11
+ * integrations: [
12
+ * new PostHogSentryIntegration(posthog)
13
+ * ]
14
+ * })
15
+ *
16
+ * Sentry.setTag(PostHogSentryIntegration.POSTHOG_ID_TAG, 'some distinct id');
17
+ *
18
+ * @param {Object} [posthog] The posthog object
19
+ * @param {string} [organization] Optional: The Sentry organization, used to send a direct link from PostHog to Sentry
20
+ * @param {Number} [projectId] Optional: The Sentry project id, used to send a direct link from PostHog to Sentry
21
+ * @param {string} [prefix] Optional: Url of a self-hosted sentry instance (default: https://sentry.io/organizations/)
22
+ * @param {SeverityLevel[] | '*'} [severityAllowList] Optional: send events matching the provided levels. Use '*' to send all events (default: ['error'])
23
+ * @param {boolean} [sendExceptionsToPostHog] Optional: capture exceptions as events in PostHog (default: true)
24
+ */
25
+ import { ErrorTracking as CoreErrorTracking } from '@posthog/core';
26
+ import { type PostHogBackendClient } from '../client';
27
+ type _SentryEvent = any;
28
+ type _SentryEventProcessor = any;
29
+ type _SentryHub = any;
30
+ interface _SentryIntegration {
31
+ name: string;
32
+ processEvent(event: _SentryEvent): _SentryEvent;
33
+ }
34
+ interface _SentryIntegrationClass {
35
+ name: string;
36
+ setupOnce(addGlobalEventProcessor: (callback: _SentryEventProcessor) => void, getCurrentHub: () => _SentryHub): void;
37
+ }
38
+ export type SentryIntegrationOptions = {
39
+ organization?: string;
40
+ projectId?: number;
41
+ prefix?: string;
42
+ severityAllowList?: CoreErrorTracking.SeverityLevel[] | '*';
43
+ sendExceptionsToPostHog?: boolean;
44
+ };
45
+ export declare function createEventProcessor(_posthog: PostHogBackendClient, { organization, projectId, prefix, severityAllowList, sendExceptionsToPostHog, }?: SentryIntegrationOptions): (event: _SentryEvent) => _SentryEvent;
46
+ export declare function sentryIntegration(_posthog: PostHogBackendClient, options?: SentryIntegrationOptions): _SentryIntegration;
47
+ export declare class PostHogSentryIntegration implements _SentryIntegrationClass {
48
+ readonly name = "posthog-node";
49
+ static readonly POSTHOG_ID_TAG = "posthog_distinct_id";
50
+ setupOnce: (addGlobalEventProcessor: (callback: _SentryEventProcessor) => void, getCurrentHub: () => _SentryHub) => void;
51
+ constructor(_posthog: PostHogBackendClient, organization?: string, prefix?: string, severityAllowList?: CoreErrorTracking.SeverityLevel[] | '*', sendExceptionsToPostHog?: boolean);
52
+ }
53
+ export {};
54
+ //# sourceMappingURL=sentry-integration.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sentry-integration.d.ts","sourceRoot":"","sources":["../../src/extensions/sentry-integration.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,EAAE,aAAa,IAAI,iBAAiB,EAAE,MAAM,eAAe,CAAA;AAClE,OAAO,EAAE,KAAK,oBAAoB,EAAE,MAAM,WAAW,CAAA;AAiBrD,KAAK,YAAY,GAAG,GAAG,CAAA;AACvB,KAAK,qBAAqB,GAAG,GAAG,CAAA;AAEhC,KAAK,UAAU,GAAG,GAAG,CAAA;AAGrB,UAAU,kBAAkB;IAC1B,IAAI,EAAE,MAAM,CAAA;IACZ,YAAY,CAAC,KAAK,EAAE,YAAY,GAAG,YAAY,CAAA;CAChD;AAED,UAAU,uBAAuB;IAC/B,IAAI,EAAE,MAAM,CAAA;IACZ,SAAS,CAAC,uBAAuB,EAAE,CAAC,QAAQ,EAAE,qBAAqB,KAAK,IAAI,EAAE,aAAa,EAAE,MAAM,UAAU,GAAG,IAAI,CAAA;CACrH;AAWD,MAAM,MAAM,wBAAwB,GAAG;IACrC,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,iBAAiB,CAAC,EAAE,iBAAiB,CAAC,aAAa,EAAE,GAAG,GAAG,CAAA;IAC3D,uBAAuB,CAAC,EAAE,OAAO,CAAA;CAClC,CAAA;AAID,wBAAgB,oBAAoB,CAClC,QAAQ,EAAE,oBAAoB,EAC9B,EACE,YAAY,EACZ,SAAS,EACT,MAAM,EACN,iBAA6B,EAC7B,uBAA8B,GAC/B,GAAE,wBAA6B,GAC/B,CAAC,KAAK,EAAE,YAAY,KAAK,YAAY,CA4EvC;AAGD,wBAAgB,iBAAiB,CAC/B,QAAQ,EAAE,oBAAoB,EAC9B,OAAO,CAAC,EAAE,wBAAwB,GACjC,kBAAkB,CAQpB;AAGD,qBAAa,wBAAyB,YAAW,uBAAuB;IACtE,SAAgB,IAAI,kBAAO;IAE3B,gBAAuB,cAAc,yBAAwB;IAEtD,SAAS,EAAE,CAChB,uBAAuB,EAAE,CAAC,QAAQ,EAAE,qBAAqB,KAAK,IAAI,EAClE,aAAa,EAAE,MAAM,UAAU,KAC5B,IAAI,CAAA;gBAGP,QAAQ,EAAE,oBAAoB,EAC9B,YAAY,CAAC,EAAE,MAAM,EACrB,MAAM,CAAC,EAAE,MAAM,EACf,iBAAiB,CAAC,EAAE,iBAAiB,CAAC,aAAa,EAAE,GAAG,GAAG,EAC3D,uBAAuB,CAAC,EAAE,OAAO;CAoBpC"}