posthog-node 3.2.1 → 3.4.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 +9 -0
- package/index.ts +1 -0
- package/lib/index.cjs.js +195 -14
- package/lib/index.cjs.js.map +1 -1
- package/lib/index.d.ts +42 -2
- package/lib/index.esm.js +195 -15
- package/lib/index.esm.js.map +1 -1
- package/lib/posthog-node/index.d.ts +1 -0
- package/lib/posthog-node/src/extensions/sentry-integration.d.ts +40 -0
- package/lib/posthog-node/src/feature-flags.d.ts +2 -1
- package/lib/posthog-node/src/posthog-node.d.ts +1 -1
- package/package.json +6 -2
- package/src/extensions/sentry-integration.ts +125 -0
- package/src/feature-flags.ts +106 -13
- package/src/posthog-node.ts +1 -1
- package/test/extensions/sentry-integration.spec.ts +150 -0
- package/test/feature-flags.spec.ts +256 -3
package/lib/index.d.ts
CHANGED
|
@@ -316,7 +316,7 @@ declare class PostHog extends PostHogCoreStateless implements PostHogNodeV1 {
|
|
|
316
316
|
private _memoryStorage;
|
|
317
317
|
private featureFlagsPoller?;
|
|
318
318
|
private maxCacheSize;
|
|
319
|
-
|
|
319
|
+
readonly options: PostHogOptions;
|
|
320
320
|
distinctIdHasSentFlagCalls: Record<string, string[]>;
|
|
321
321
|
constructor(apiKey: string, options?: PostHogOptions);
|
|
322
322
|
getPersistedProperty(key: PostHogPersistedProperty): any | undefined;
|
|
@@ -379,4 +379,44 @@ declare class PostHog extends PostHogCoreStateless implements PostHogNodeV1 {
|
|
|
379
379
|
shutdownAsync(): Promise<void>;
|
|
380
380
|
}
|
|
381
381
|
|
|
382
|
-
|
|
382
|
+
/**
|
|
383
|
+
* @file Adapted from [posthog-js](https://github.com/PostHog/posthog-js/blob/8157df935a4d0e71d2fefef7127aa85ee51c82d1/src/extensions/sentry-integration.ts) with modifications for the Node SDK.
|
|
384
|
+
*/
|
|
385
|
+
|
|
386
|
+
declare type _SentryEventProcessor = any;
|
|
387
|
+
declare type _SentryHub = any;
|
|
388
|
+
interface _SentryIntegration {
|
|
389
|
+
name: string;
|
|
390
|
+
setupOnce(addGlobalEventProcessor: (callback: _SentryEventProcessor) => void, getCurrentHub: () => _SentryHub): void;
|
|
391
|
+
}
|
|
392
|
+
/**
|
|
393
|
+
* Integrate Sentry with PostHog. This will add a direct link to the person in Sentry, and an $exception event in PostHog.
|
|
394
|
+
*
|
|
395
|
+
* ### Usage
|
|
396
|
+
*
|
|
397
|
+
* Sentry.init({
|
|
398
|
+
* dsn: 'https://example',
|
|
399
|
+
* integrations: [
|
|
400
|
+
* new PostHogSentryIntegration(posthog)
|
|
401
|
+
* ]
|
|
402
|
+
* })
|
|
403
|
+
*
|
|
404
|
+
* Sentry.setTag(PostHogSentryIntegration.POSTHOG_ID_TAG, 'some distinct id');
|
|
405
|
+
*
|
|
406
|
+
* @param {Object} [posthog] The posthog object
|
|
407
|
+
* @param {string} [organization] Optional: The Sentry organization, used to send a direct link from PostHog to Sentry
|
|
408
|
+
* @param {Number} [projectId] Optional: The Sentry project id, used to send a direct link from PostHog to Sentry
|
|
409
|
+
* @param {string} [prefix] Optional: Url of a self-hosted sentry instance (default: https://sentry.io/organizations/)
|
|
410
|
+
*/
|
|
411
|
+
declare class PostHogSentryIntegration implements _SentryIntegration {
|
|
412
|
+
private readonly posthog;
|
|
413
|
+
private readonly posthogHost?;
|
|
414
|
+
private readonly organization?;
|
|
415
|
+
private readonly prefix?;
|
|
416
|
+
readonly name = "posthog-node";
|
|
417
|
+
static readonly POSTHOG_ID_TAG = "posthog_distinct_id";
|
|
418
|
+
constructor(posthog: PostHog, posthogHost?: string | undefined, organization?: string | undefined, prefix?: string | undefined);
|
|
419
|
+
setupOnce(addGlobalEventProcessor: (callback: _SentryEventProcessor) => void, getCurrentHub: () => _SentryHub): void;
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
export { PostHog, PostHogOptions, PostHogSentryIntegration };
|
package/lib/index.esm.js
CHANGED
|
@@ -154,7 +154,7 @@ function __spreadArray(to, from, pack) {
|
|
|
154
154
|
return to.concat(ar || Array.prototype.slice.call(from));
|
|
155
155
|
}
|
|
156
156
|
|
|
157
|
-
var version = "3.
|
|
157
|
+
var version = "3.4.0";
|
|
158
158
|
|
|
159
159
|
var PostHogPersistedProperty;
|
|
160
160
|
(function (PostHogPersistedProperty) {
|
|
@@ -2344,12 +2344,36 @@ function matchProperty(property, propertyValues) {
|
|
|
2344
2344
|
|
|
2345
2345
|
var overrideValue = propertyValues[key];
|
|
2346
2346
|
|
|
2347
|
+
function computeExactMatch(value, overrideValue) {
|
|
2348
|
+
if (Array.isArray(value)) {
|
|
2349
|
+
return value.map(function (val) {
|
|
2350
|
+
return String(val).toLowerCase();
|
|
2351
|
+
}).includes(String(overrideValue).toLowerCase());
|
|
2352
|
+
}
|
|
2353
|
+
|
|
2354
|
+
return String(value).toLowerCase() === String(overrideValue).toLowerCase();
|
|
2355
|
+
}
|
|
2356
|
+
|
|
2357
|
+
function compare(lhs, rhs, operator) {
|
|
2358
|
+
if (operator === 'gt') {
|
|
2359
|
+
return lhs > rhs;
|
|
2360
|
+
} else if (operator === 'gte') {
|
|
2361
|
+
return lhs >= rhs;
|
|
2362
|
+
} else if (operator === 'lt') {
|
|
2363
|
+
return lhs < rhs;
|
|
2364
|
+
} else if (operator === 'lte') {
|
|
2365
|
+
return lhs <= rhs;
|
|
2366
|
+
} else {
|
|
2367
|
+
throw new Error("Invalid operator: ".concat(operator));
|
|
2368
|
+
}
|
|
2369
|
+
}
|
|
2370
|
+
|
|
2347
2371
|
switch (operator) {
|
|
2348
2372
|
case 'exact':
|
|
2349
|
-
return
|
|
2373
|
+
return computeExactMatch(value, overrideValue);
|
|
2350
2374
|
|
|
2351
2375
|
case 'is_not':
|
|
2352
|
-
return
|
|
2376
|
+
return !computeExactMatch(value, overrideValue);
|
|
2353
2377
|
|
|
2354
2378
|
case 'is_set':
|
|
2355
2379
|
return key in propertyValues;
|
|
@@ -2367,24 +2391,53 @@ function matchProperty(property, propertyValues) {
|
|
|
2367
2391
|
return isValidRegex(String(value)) && String(overrideValue).match(String(value)) === null;
|
|
2368
2392
|
|
|
2369
2393
|
case 'gt':
|
|
2370
|
-
return typeof overrideValue == typeof value && overrideValue > value;
|
|
2371
|
-
|
|
2372
2394
|
case 'gte':
|
|
2373
|
-
return typeof overrideValue == typeof value && overrideValue >= value;
|
|
2374
|
-
|
|
2375
2395
|
case 'lt':
|
|
2376
|
-
return typeof overrideValue == typeof value && overrideValue < value;
|
|
2377
|
-
|
|
2378
2396
|
case 'lte':
|
|
2379
|
-
|
|
2397
|
+
{
|
|
2398
|
+
// :TRICKY: We adjust comparison based on the override value passed in,
|
|
2399
|
+
// to make sure we handle both numeric and string comparisons appropriately.
|
|
2400
|
+
var parsedValue = typeof value === 'number' ? value : null;
|
|
2401
|
+
|
|
2402
|
+
if (typeof value === 'string') {
|
|
2403
|
+
try {
|
|
2404
|
+
parsedValue = parseFloat(value);
|
|
2405
|
+
} catch (err) {// pass
|
|
2406
|
+
}
|
|
2407
|
+
}
|
|
2408
|
+
|
|
2409
|
+
if (parsedValue != null && overrideValue != null) {
|
|
2410
|
+
// check both null and undefined
|
|
2411
|
+
if (typeof overrideValue === 'string') {
|
|
2412
|
+
return compare(overrideValue, String(value), operator);
|
|
2413
|
+
} else {
|
|
2414
|
+
return compare(overrideValue, parsedValue, operator);
|
|
2415
|
+
}
|
|
2416
|
+
} else {
|
|
2417
|
+
return compare(String(overrideValue), String(value), operator);
|
|
2418
|
+
}
|
|
2419
|
+
}
|
|
2380
2420
|
|
|
2381
2421
|
case 'is_date_after':
|
|
2382
2422
|
case 'is_date_before':
|
|
2423
|
+
case 'is_relative_date_before':
|
|
2424
|
+
case 'is_relative_date_after':
|
|
2383
2425
|
{
|
|
2384
|
-
var parsedDate =
|
|
2426
|
+
var parsedDate = null;
|
|
2427
|
+
|
|
2428
|
+
if (['is_relative_date_before', 'is_relative_date_after'].includes(operator)) {
|
|
2429
|
+
parsedDate = relativeDateParseForFeatureFlagMatching(String(value));
|
|
2430
|
+
} else {
|
|
2431
|
+
parsedDate = convertToDateTime(value);
|
|
2432
|
+
}
|
|
2433
|
+
|
|
2434
|
+
if (parsedDate == null) {
|
|
2435
|
+
throw new InconclusiveMatchError("Invalid date: ".concat(value));
|
|
2436
|
+
}
|
|
2437
|
+
|
|
2385
2438
|
var overrideDate = convertToDateTime(overrideValue);
|
|
2386
2439
|
|
|
2387
|
-
if (
|
|
2440
|
+
if (['is_date_before', 'is_relative_date_before'].includes(operator)) {
|
|
2388
2441
|
return overrideDate < parsedDate;
|
|
2389
2442
|
}
|
|
2390
2443
|
|
|
@@ -2392,8 +2445,7 @@ function matchProperty(property, propertyValues) {
|
|
|
2392
2445
|
}
|
|
2393
2446
|
|
|
2394
2447
|
default:
|
|
2395
|
-
|
|
2396
|
-
return false;
|
|
2448
|
+
throw new InconclusiveMatchError("Unknown operator: ".concat(operator));
|
|
2397
2449
|
}
|
|
2398
2450
|
}
|
|
2399
2451
|
|
|
@@ -2535,6 +2587,45 @@ function convertToDateTime(value) {
|
|
|
2535
2587
|
}
|
|
2536
2588
|
}
|
|
2537
2589
|
|
|
2590
|
+
function relativeDateParseForFeatureFlagMatching(value) {
|
|
2591
|
+
var regex = /^(?<number>[0-9]+)(?<interval>[a-z])$/;
|
|
2592
|
+
var match = value.match(regex);
|
|
2593
|
+
var parsedDt = new Date(new Date().toISOString());
|
|
2594
|
+
|
|
2595
|
+
if (match) {
|
|
2596
|
+
if (!match.groups) {
|
|
2597
|
+
return null;
|
|
2598
|
+
}
|
|
2599
|
+
|
|
2600
|
+
var number = parseInt(match.groups['number']);
|
|
2601
|
+
|
|
2602
|
+
if (number >= 10000) {
|
|
2603
|
+
// Guard against overflow, disallow numbers greater than 10_000
|
|
2604
|
+
return null;
|
|
2605
|
+
}
|
|
2606
|
+
|
|
2607
|
+
var interval = match.groups['interval'];
|
|
2608
|
+
|
|
2609
|
+
if (interval == 'h') {
|
|
2610
|
+
parsedDt.setUTCHours(parsedDt.getUTCHours() - number);
|
|
2611
|
+
} else if (interval == 'd') {
|
|
2612
|
+
parsedDt.setUTCDate(parsedDt.getUTCDate() - number);
|
|
2613
|
+
} else if (interval == 'w') {
|
|
2614
|
+
parsedDt.setUTCDate(parsedDt.getUTCDate() - number * 7);
|
|
2615
|
+
} else if (interval == 'm') {
|
|
2616
|
+
parsedDt.setUTCMonth(parsedDt.getUTCMonth() - number);
|
|
2617
|
+
} else if (interval == 'y') {
|
|
2618
|
+
parsedDt.setUTCFullYear(parsedDt.getUTCFullYear() - number);
|
|
2619
|
+
} else {
|
|
2620
|
+
return null;
|
|
2621
|
+
}
|
|
2622
|
+
|
|
2623
|
+
return parsedDt;
|
|
2624
|
+
} else {
|
|
2625
|
+
return null;
|
|
2626
|
+
}
|
|
2627
|
+
}
|
|
2628
|
+
|
|
2538
2629
|
var THIRTY_SECONDS = 30 * 1000;
|
|
2539
2630
|
var MAX_CACHE_SIZE = 50 * 1000; // The actual exported Nodejs API.
|
|
2540
2631
|
|
|
@@ -3005,5 +3096,94 @@ function (_super) {
|
|
|
3005
3096
|
return PostHog;
|
|
3006
3097
|
}(PostHogCoreStateless);
|
|
3007
3098
|
|
|
3008
|
-
|
|
3099
|
+
/**
|
|
3100
|
+
* Integrate Sentry with PostHog. This will add a direct link to the person in Sentry, and an $exception event in PostHog.
|
|
3101
|
+
*
|
|
3102
|
+
* ### Usage
|
|
3103
|
+
*
|
|
3104
|
+
* Sentry.init({
|
|
3105
|
+
* dsn: 'https://example',
|
|
3106
|
+
* integrations: [
|
|
3107
|
+
* new PostHogSentryIntegration(posthog)
|
|
3108
|
+
* ]
|
|
3109
|
+
* })
|
|
3110
|
+
*
|
|
3111
|
+
* Sentry.setTag(PostHogSentryIntegration.POSTHOG_ID_TAG, 'some distinct id');
|
|
3112
|
+
*
|
|
3113
|
+
* @param {Object} [posthog] The posthog object
|
|
3114
|
+
* @param {string} [organization] Optional: The Sentry organization, used to send a direct link from PostHog to Sentry
|
|
3115
|
+
* @param {Number} [projectId] Optional: The Sentry project id, used to send a direct link from PostHog to Sentry
|
|
3116
|
+
* @param {string} [prefix] Optional: Url of a self-hosted sentry instance (default: https://sentry.io/organizations/)
|
|
3117
|
+
*/
|
|
3118
|
+
var PostHogSentryIntegration =
|
|
3119
|
+
/** @class */
|
|
3120
|
+
function () {
|
|
3121
|
+
function PostHogSentryIntegration(posthog, posthogHost, organization, prefix) {
|
|
3122
|
+
var _a;
|
|
3123
|
+
|
|
3124
|
+
this.posthog = posthog;
|
|
3125
|
+
this.posthogHost = posthogHost;
|
|
3126
|
+
this.organization = organization;
|
|
3127
|
+
this.prefix = prefix;
|
|
3128
|
+
this.name = 'posthog-node';
|
|
3129
|
+
this.posthogHost = (_a = posthog.options.host) !== null && _a !== void 0 ? _a : 'https://app.posthog.com';
|
|
3130
|
+
}
|
|
3131
|
+
|
|
3132
|
+
PostHogSentryIntegration.prototype.setupOnce = function (addGlobalEventProcessor, getCurrentHub) {
|
|
3133
|
+
var _this = this;
|
|
3134
|
+
|
|
3135
|
+
addGlobalEventProcessor(function (event) {
|
|
3136
|
+
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
3137
|
+
|
|
3138
|
+
if (((_a = event.exception) === null || _a === void 0 ? void 0 : _a.values) === undefined || event.exception.values.length === 0) {
|
|
3139
|
+
return event;
|
|
3140
|
+
}
|
|
3141
|
+
|
|
3142
|
+
if (!event.tags) {
|
|
3143
|
+
event.tags = {};
|
|
3144
|
+
}
|
|
3145
|
+
|
|
3146
|
+
var sentry = getCurrentHub(); // Get the PostHog user ID from a specific tag, which users can set on their Sentry scope as they need.
|
|
3147
|
+
|
|
3148
|
+
var userId = event.tags[PostHogSentryIntegration.POSTHOG_ID_TAG];
|
|
3149
|
+
|
|
3150
|
+
if (userId === undefined) {
|
|
3151
|
+
// If we can't find a user ID, don't bother linking the event. We won't be able to send anything meaningful to PostHog without it.
|
|
3152
|
+
return event;
|
|
3153
|
+
}
|
|
3154
|
+
|
|
3155
|
+
event.tags['PostHog Person URL'] = new URL("/person/".concat(userId), _this.posthogHost).toString();
|
|
3156
|
+
var properties = {
|
|
3157
|
+
// PostHog Exception Properties
|
|
3158
|
+
$exception_message: (_b = event.exception.values[0]) === null || _b === void 0 ? void 0 : _b.value,
|
|
3159
|
+
$exception_type: (_c = event.exception.values[0]) === null || _c === void 0 ? void 0 : _c.type,
|
|
3160
|
+
$exception_personURL: event.tags['PostHog Person URL'],
|
|
3161
|
+
// Sentry Exception Properties
|
|
3162
|
+
$sentry_event_id: event.event_id,
|
|
3163
|
+
$sentry_exception: event.exception,
|
|
3164
|
+
$sentry_exception_message: (_d = event.exception.values[0]) === null || _d === void 0 ? void 0 : _d.value,
|
|
3165
|
+
$sentry_exception_type: (_e = event.exception.values[0]) === null || _e === void 0 ? void 0 : _e.type,
|
|
3166
|
+
$sentry_tags: event.tags
|
|
3167
|
+
};
|
|
3168
|
+
var projectId = (_g = (_f = sentry.getClient()) === null || _f === void 0 ? void 0 : _f.getDsn()) === null || _g === void 0 ? void 0 : _g.projectId;
|
|
3169
|
+
|
|
3170
|
+
if (_this.organization !== undefined && projectId !== undefined && event.event_id !== undefined) {
|
|
3171
|
+
properties.$sentry_url = "".concat((_h = _this.prefix) !== null && _h !== void 0 ? _h : 'https://sentry.io/organizations', "/").concat(_this.organization, "/issues/?project=").concat(projectId, "&query=").concat(event.event_id);
|
|
3172
|
+
}
|
|
3173
|
+
|
|
3174
|
+
_this.posthog.capture({
|
|
3175
|
+
event: '$exception',
|
|
3176
|
+
distinctId: userId,
|
|
3177
|
+
properties: properties
|
|
3178
|
+
});
|
|
3179
|
+
|
|
3180
|
+
return event;
|
|
3181
|
+
});
|
|
3182
|
+
};
|
|
3183
|
+
|
|
3184
|
+
PostHogSentryIntegration.POSTHOG_ID_TAG = 'posthog_distinct_id';
|
|
3185
|
+
return PostHogSentryIntegration;
|
|
3186
|
+
}();
|
|
3187
|
+
|
|
3188
|
+
export { PostHog, PostHogSentryIntegration };
|
|
3009
3189
|
//# sourceMappingURL=index.esm.js.map
|