posthog-node 4.14.0 → 4.16.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/lib/index.cjs.js +328 -77
- package/lib/index.cjs.js.map +1 -1
- package/lib/index.d.ts +51 -2
- package/lib/index.esm.js +328 -77
- package/lib/index.esm.js.map +1 -1
- package/lib/posthog-core/src/index.d.ts +17 -0
- package/lib/posthog-core/src/utils.d.ts +2 -1
- package/lib/posthog-node/src/posthog-node.d.ts +8 -1
- package/lib/posthog-node/src/types.d.ts +26 -0
- package/lib/posthog-node/test/test-utils.d.ts +1 -0
- package/package.json +1 -1
- package/src/posthog-node.ts +102 -2
- package/src/types.ts +26 -0
- package/test/posthog-node.spec.ts +21 -23
- package/test/test-utils.ts +5 -0
|
@@ -3,6 +3,7 @@ import { RetriableOptions } from './utils';
|
|
|
3
3
|
import { LZString } from './lz-string';
|
|
4
4
|
import { SimpleEventEmitter } from './eventemitter';
|
|
5
5
|
export * as utils from './utils';
|
|
6
|
+
export declare function logFlushError(err: any): Promise<void>;
|
|
6
7
|
export declare abstract class PostHogCoreStateless {
|
|
7
8
|
readonly apiKey: string;
|
|
8
9
|
readonly host: string;
|
|
@@ -13,6 +14,7 @@ export declare abstract class PostHogCoreStateless {
|
|
|
13
14
|
private maxQueueSize;
|
|
14
15
|
private flushInterval;
|
|
15
16
|
private flushPromise;
|
|
17
|
+
private shutdownPromise;
|
|
16
18
|
private requestTimeout;
|
|
17
19
|
private featureFlagsRequestTimeoutMs;
|
|
18
20
|
private remoteConfigRequestTimeoutMs;
|
|
@@ -52,12 +54,19 @@ export declare abstract class PostHogCoreStateless {
|
|
|
52
54
|
*** TRACKING
|
|
53
55
|
***/
|
|
54
56
|
protected identifyStateless(distinctId: string, properties?: PostHogEventProperties, options?: PostHogCaptureOptions): void;
|
|
57
|
+
protected identifyStatelessImmediate(distinctId: string, properties?: PostHogEventProperties, options?: PostHogCaptureOptions): Promise<void>;
|
|
55
58
|
protected captureStateless(distinctId: string, event: string, properties?: {
|
|
56
59
|
[key: string]: any;
|
|
57
60
|
}, options?: PostHogCaptureOptions): void;
|
|
61
|
+
protected captureStatelessImmediate(distinctId: string, event: string, properties?: {
|
|
62
|
+
[key: string]: any;
|
|
63
|
+
}, options?: PostHogCaptureOptions): Promise<void>;
|
|
58
64
|
protected aliasStateless(alias: string, distinctId: string, properties?: {
|
|
59
65
|
[key: string]: any;
|
|
60
66
|
}, options?: PostHogCaptureOptions): void;
|
|
67
|
+
protected aliasStatelessImmediate(alias: string, distinctId: string, properties?: {
|
|
68
|
+
[key: string]: any;
|
|
69
|
+
}, options?: PostHogCaptureOptions): Promise<void>;
|
|
61
70
|
/***
|
|
62
71
|
*** GROUPS
|
|
63
72
|
***/
|
|
@@ -106,6 +115,8 @@ export declare abstract class PostHogCoreStateless {
|
|
|
106
115
|
*** QUEUEING AND FLUSHING
|
|
107
116
|
***/
|
|
108
117
|
protected enqueue(type: string, _message: any, options?: PostHogCaptureOptions): void;
|
|
118
|
+
protected sendImmediate(type: string, _message: any, options?: PostHogCaptureOptions): Promise<void>;
|
|
119
|
+
private prepareMessage;
|
|
109
120
|
private clearFlushTimer;
|
|
110
121
|
/**
|
|
111
122
|
* Helper for flushing the queue in the background
|
|
@@ -118,6 +129,12 @@ export declare abstract class PostHogCoreStateless {
|
|
|
118
129
|
};
|
|
119
130
|
private _flush;
|
|
120
131
|
private fetchWithRetry;
|
|
132
|
+
_shutdown(shutdownTimeoutMs?: number): Promise<void>;
|
|
133
|
+
/**
|
|
134
|
+
* Call shutdown() once before the node process exits, so ensure that all events have been sent and all promises
|
|
135
|
+
* have resolved. Do not use this function if you intend to keep using this PostHog instance after calling it.
|
|
136
|
+
* @param shutdownTimeoutMs
|
|
137
|
+
*/
|
|
121
138
|
shutdown(shutdownTimeoutMs?: number): Promise<void>;
|
|
122
139
|
}
|
|
123
140
|
export declare abstract class PostHogCore extends PostHogCoreStateless {
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import { FetchLike } from './types';
|
|
2
2
|
export declare const NEW_FLAGS_ROLLOUT_PERCENTAGE = 1;
|
|
3
3
|
export declare const NEW_FLAGS_EXCLUDED_HASHES: Set<string>;
|
|
4
|
+
export declare const STRING_FORMAT = "utf8";
|
|
4
5
|
export declare function assert(truthyValue: any, message: string): void;
|
|
5
6
|
export declare function removeTrailingSlash(url: string): string;
|
|
6
7
|
export interface RetriableOptions {
|
|
7
8
|
retryCount: number;
|
|
8
9
|
retryDelay: number;
|
|
9
|
-
retryCheck: (err:
|
|
10
|
+
retryCheck: (err: unknown) => boolean;
|
|
10
11
|
}
|
|
11
12
|
export declare function retriable<T>(fn: () => Promise<T>, props: RetriableOptions): Promise<T>;
|
|
12
13
|
export declare function currentTimestamp(): number;
|
|
@@ -32,12 +32,19 @@ export declare class PostHog extends PostHogCoreStateless implements PostHogNode
|
|
|
32
32
|
disable(): Promise<void>;
|
|
33
33
|
debug(enabled?: boolean): void;
|
|
34
34
|
capture(props: EventMessage): void;
|
|
35
|
+
captureImmediate(props: EventMessage): Promise<void>;
|
|
35
36
|
identify({ distinctId, properties, disableGeoip }: IdentifyMessage): void;
|
|
37
|
+
identifyImmediate({ distinctId, properties, disableGeoip }: IdentifyMessage): Promise<void>;
|
|
36
38
|
alias(data: {
|
|
37
39
|
distinctId: string;
|
|
38
40
|
alias: string;
|
|
39
41
|
disableGeoip?: boolean;
|
|
40
42
|
}): void;
|
|
43
|
+
aliasImmediate(data: {
|
|
44
|
+
distinctId: string;
|
|
45
|
+
alias: string;
|
|
46
|
+
disableGeoip?: boolean;
|
|
47
|
+
}): Promise<void>;
|
|
41
48
|
isLocalEvaluationReady(): boolean;
|
|
42
49
|
waitForLocalEvaluationReady(timeoutMs?: number): Promise<boolean>;
|
|
43
50
|
getFeatureFlag(key: string, distinctId: string, options?: {
|
|
@@ -85,7 +92,7 @@ export declare class PostHog extends PostHogCoreStateless implements PostHogNode
|
|
|
85
92
|
* This is useful to call if you want to ensure that the feature flags are up to date before calling getFeatureFlag.
|
|
86
93
|
*/
|
|
87
94
|
reloadFeatureFlags(): Promise<void>;
|
|
88
|
-
|
|
95
|
+
_shutdown(shutdownTimeoutMs?: number): Promise<void>;
|
|
89
96
|
private addLocalPersonAndGroupProperties;
|
|
90
97
|
captureException(error: unknown, distinctId?: string, additionalProperties?: Record<string | number, any>): void;
|
|
91
98
|
}
|
|
@@ -70,6 +70,15 @@ export type PostHogNodeV1 = {
|
|
|
70
70
|
* @param sendFeatureFlags OPTIONAL | Used with experiments. Determines whether to send feature flag values with the event.
|
|
71
71
|
*/
|
|
72
72
|
capture({ distinctId, event, properties, groups, sendFeatureFlags }: EventMessage): void;
|
|
73
|
+
/**
|
|
74
|
+
* @description Capture an event immediately. Useful for edge environments where the usual queue-based sending is not preferable. Do not mix immediate and non-immediate calls.
|
|
75
|
+
* @param distinctId which uniquely identifies your user
|
|
76
|
+
* @param event We recommend using [verb] [noun], like movie played or movie updated to easily identify what your events mean later on.
|
|
77
|
+
* @param properties OPTIONAL | which can be a object with any information you'd like to add
|
|
78
|
+
* @param groups OPTIONAL | object of what groups are related to this event, example: { company: 'id:5' }. Can be used to analyze companies instead of users.
|
|
79
|
+
* @param sendFeatureFlags OPTIONAL | Used with experiments. Determines whether to send feature flag values with the event.
|
|
80
|
+
*/
|
|
81
|
+
captureImmediate({ distinctId, event, properties, groups, sendFeatureFlags }: EventMessage): Promise<void>;
|
|
73
82
|
/**
|
|
74
83
|
* @description Identify lets you add metadata on your users so you can more easily identify who they are in PostHog,
|
|
75
84
|
* and even do things like segment users by these properties.
|
|
@@ -78,6 +87,13 @@ export type PostHogNodeV1 = {
|
|
|
78
87
|
* @param properties with a dict with any key: value pairs
|
|
79
88
|
*/
|
|
80
89
|
identify({ distinctId, properties }: IdentifyMessage): void;
|
|
90
|
+
/**
|
|
91
|
+
* @description Identify lets you add metadata on your users so you can more easily identify who they are in PostHog.
|
|
92
|
+
* Useful for edge environments where the usual queue-based sending is not preferable. Do not mix immediate and non-immediate calls.
|
|
93
|
+
* @param distinctId which uniquely identifies your user
|
|
94
|
+
* @param properties with a dict with any key: value pairs
|
|
95
|
+
*/
|
|
96
|
+
identifyImmediate({ distinctId, properties }: IdentifyMessage): Promise<void>;
|
|
81
97
|
/**
|
|
82
98
|
* @description To marry up whatever a user does before they sign up or log in with what they do after you need to make an alias call.
|
|
83
99
|
* This will allow you to answer questions like "Which marketing channels leads to users churning after a month?"
|
|
@@ -93,6 +109,16 @@ export type PostHogNodeV1 = {
|
|
|
93
109
|
distinctId: string;
|
|
94
110
|
alias: string;
|
|
95
111
|
}): void;
|
|
112
|
+
/**
|
|
113
|
+
* @description To marry up whatever a user does before they sign up or log in with what they do after you need to make an alias call.
|
|
114
|
+
* Useful for edge environments where the usual queue-based sending is not preferable. Do not mix immediate and non-immediate calls.
|
|
115
|
+
* @param distinctId the current unique id
|
|
116
|
+
* @param alias the unique ID of the user before
|
|
117
|
+
*/
|
|
118
|
+
aliasImmediate(data: {
|
|
119
|
+
distinctId: string;
|
|
120
|
+
alias: string;
|
|
121
|
+
}): Promise<void>;
|
|
96
122
|
/**
|
|
97
123
|
* @description PostHog feature flags (https://posthog.com/docs/features/feature-flags)
|
|
98
124
|
* allow you to safely deploy and roll back new features. Once you've created a feature flag in PostHog,
|
|
@@ -14,4 +14,5 @@ export declare const apiImplementation: ({ localFlags, decideFlags, decideFlagPa
|
|
|
14
14
|
}) => (url: any) => Promise<any>;
|
|
15
15
|
export declare const anyLocalEvalCall: any[];
|
|
16
16
|
export declare const anyDecideCall: any[];
|
|
17
|
+
export declare const isPending: (promise: Promise<any>) => boolean;
|
|
17
18
|
export {};
|
package/package.json
CHANGED
package/src/posthog-node.ts
CHANGED
|
@@ -199,6 +199,84 @@ export class PostHog extends PostHogCoreStateless implements PostHogNodeV1 {
|
|
|
199
199
|
this.addPendingPromise(capturePromise)
|
|
200
200
|
}
|
|
201
201
|
|
|
202
|
+
async captureImmediate(props: EventMessage): Promise<void> {
|
|
203
|
+
if (typeof props === 'string') {
|
|
204
|
+
this.logMsgIfDebug(() =>
|
|
205
|
+
console.warn('Called capture() with a string as the first argument when an object was expected.')
|
|
206
|
+
)
|
|
207
|
+
}
|
|
208
|
+
const { distinctId, event, properties, groups, sendFeatureFlags, timestamp, disableGeoip, uuid }: EventMessage =
|
|
209
|
+
props
|
|
210
|
+
|
|
211
|
+
const _capture = (props: EventMessage['properties']): Promise<void> => {
|
|
212
|
+
return super.captureStatelessImmediate(distinctId, event, props, { timestamp, disableGeoip, uuid })
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
const _getFlags = async (
|
|
216
|
+
distinctId: EventMessage['distinctId'],
|
|
217
|
+
groups: EventMessage['groups'],
|
|
218
|
+
disableGeoip: EventMessage['disableGeoip']
|
|
219
|
+
): Promise<PostHogDecideResponse['featureFlags'] | undefined> => {
|
|
220
|
+
return (await super.getFeatureFlagsStateless(distinctId, groups, undefined, undefined, disableGeoip)).flags
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
const capturePromise = Promise.resolve()
|
|
224
|
+
.then(async () => {
|
|
225
|
+
if (sendFeatureFlags) {
|
|
226
|
+
// If we are sending feature flags, we need to make sure we have the latest flags
|
|
227
|
+
// return await super.getFeatureFlagsStateless(distinctId, groups, undefined, undefined, disableGeoip)
|
|
228
|
+
return await _getFlags(distinctId, groups, disableGeoip)
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
if (event === '$feature_flag_called') {
|
|
232
|
+
// If we're capturing a $feature_flag_called event, we don't want to enrich the event with cached flags that may be out of date.
|
|
233
|
+
return {}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
if ((this.featureFlagsPoller?.featureFlags?.length || 0) > 0) {
|
|
237
|
+
// Otherwise we may as well check for the flags locally and include them if they are already loaded
|
|
238
|
+
const groupsWithStringValues: Record<string, string> = {}
|
|
239
|
+
for (const [key, value] of Object.entries(groups || {})) {
|
|
240
|
+
groupsWithStringValues[key] = String(value)
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
return await this.getAllFlags(distinctId, {
|
|
244
|
+
groups: groupsWithStringValues,
|
|
245
|
+
disableGeoip,
|
|
246
|
+
onlyEvaluateLocally: true,
|
|
247
|
+
})
|
|
248
|
+
}
|
|
249
|
+
return {}
|
|
250
|
+
})
|
|
251
|
+
.then((flags) => {
|
|
252
|
+
// Derive the relevant flag properties to add
|
|
253
|
+
const additionalProperties: Record<string, any> = {}
|
|
254
|
+
if (flags) {
|
|
255
|
+
for (const [feature, variant] of Object.entries(flags)) {
|
|
256
|
+
additionalProperties[`$feature/${feature}`] = variant
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
const activeFlags = Object.keys(flags || {})
|
|
260
|
+
.filter((flag) => flags?.[flag] !== false)
|
|
261
|
+
.sort()
|
|
262
|
+
if (activeFlags.length > 0) {
|
|
263
|
+
additionalProperties['$active_feature_flags'] = activeFlags
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
return additionalProperties
|
|
267
|
+
})
|
|
268
|
+
.catch(() => {
|
|
269
|
+
// Something went wrong getting the flag info - we should capture the event anyways
|
|
270
|
+
return {}
|
|
271
|
+
})
|
|
272
|
+
.then((additionalProperties) => {
|
|
273
|
+
// No matter what - capture the event
|
|
274
|
+
_capture({ ...additionalProperties, ...properties, $groups: groups })
|
|
275
|
+
})
|
|
276
|
+
|
|
277
|
+
await capturePromise
|
|
278
|
+
}
|
|
279
|
+
|
|
202
280
|
identify({ distinctId, properties, disableGeoip }: IdentifyMessage): void {
|
|
203
281
|
// Catch properties passed as $set and move them to the top level
|
|
204
282
|
|
|
@@ -219,10 +297,32 @@ export class PostHog extends PostHogCoreStateless implements PostHogNodeV1 {
|
|
|
219
297
|
)
|
|
220
298
|
}
|
|
221
299
|
|
|
300
|
+
async identifyImmediate({ distinctId, properties, disableGeoip }: IdentifyMessage): Promise<void> {
|
|
301
|
+
// promote $set and $set_once to top level
|
|
302
|
+
const userPropsOnce = properties?.$set_once
|
|
303
|
+
delete properties?.$set_once
|
|
304
|
+
|
|
305
|
+
// if no $set is provided we assume all properties are $set
|
|
306
|
+
const userProps = properties?.$set || properties
|
|
307
|
+
|
|
308
|
+
await super.identifyStatelessImmediate(
|
|
309
|
+
distinctId,
|
|
310
|
+
{
|
|
311
|
+
$set: userProps,
|
|
312
|
+
$set_once: userPropsOnce,
|
|
313
|
+
},
|
|
314
|
+
{ disableGeoip }
|
|
315
|
+
)
|
|
316
|
+
}
|
|
317
|
+
|
|
222
318
|
alias(data: { distinctId: string; alias: string; disableGeoip?: boolean }): void {
|
|
223
319
|
super.aliasStateless(data.alias, data.distinctId, undefined, { disableGeoip: data.disableGeoip })
|
|
224
320
|
}
|
|
225
321
|
|
|
322
|
+
async aliasImmediate(data: { distinctId: string; alias: string; disableGeoip?: boolean }): Promise<void> {
|
|
323
|
+
await super.aliasStatelessImmediate(data.alias, data.distinctId, undefined, { disableGeoip: data.disableGeoip })
|
|
324
|
+
}
|
|
325
|
+
|
|
226
326
|
isLocalEvaluationReady(): boolean {
|
|
227
327
|
return this.featureFlagsPoller?.isLocalEvaluationReady() ?? false
|
|
228
328
|
}
|
|
@@ -535,9 +635,9 @@ export class PostHog extends PostHogCoreStateless implements PostHogNodeV1 {
|
|
|
535
635
|
await this.featureFlagsPoller?.loadFeatureFlags(true)
|
|
536
636
|
}
|
|
537
637
|
|
|
538
|
-
async
|
|
638
|
+
async _shutdown(shutdownTimeoutMs?: number): Promise<void> {
|
|
539
639
|
this.featureFlagsPoller?.stopPoller()
|
|
540
|
-
return super.
|
|
640
|
+
return super._shutdown(shutdownTimeoutMs)
|
|
541
641
|
}
|
|
542
642
|
|
|
543
643
|
private addLocalPersonAndGroupProperties(
|
package/src/types.ts
CHANGED
|
@@ -79,6 +79,16 @@ export type PostHogNodeV1 = {
|
|
|
79
79
|
*/
|
|
80
80
|
capture({ distinctId, event, properties, groups, sendFeatureFlags }: EventMessage): void
|
|
81
81
|
|
|
82
|
+
/**
|
|
83
|
+
* @description Capture an event immediately. Useful for edge environments where the usual queue-based sending is not preferable. Do not mix immediate and non-immediate calls.
|
|
84
|
+
* @param distinctId which uniquely identifies your user
|
|
85
|
+
* @param event We recommend using [verb] [noun], like movie played or movie updated to easily identify what your events mean later on.
|
|
86
|
+
* @param properties OPTIONAL | which can be a object with any information you'd like to add
|
|
87
|
+
* @param groups OPTIONAL | object of what groups are related to this event, example: { company: 'id:5' }. Can be used to analyze companies instead of users.
|
|
88
|
+
* @param sendFeatureFlags OPTIONAL | Used with experiments. Determines whether to send feature flag values with the event.
|
|
89
|
+
*/
|
|
90
|
+
captureImmediate({ distinctId, event, properties, groups, sendFeatureFlags }: EventMessage): Promise<void>
|
|
91
|
+
|
|
82
92
|
/**
|
|
83
93
|
* @description Identify lets you add metadata on your users so you can more easily identify who they are in PostHog,
|
|
84
94
|
* and even do things like segment users by these properties.
|
|
@@ -88,6 +98,14 @@ export type PostHogNodeV1 = {
|
|
|
88
98
|
*/
|
|
89
99
|
identify({ distinctId, properties }: IdentifyMessage): void
|
|
90
100
|
|
|
101
|
+
/**
|
|
102
|
+
* @description Identify lets you add metadata on your users so you can more easily identify who they are in PostHog.
|
|
103
|
+
* Useful for edge environments where the usual queue-based sending is not preferable. Do not mix immediate and non-immediate calls.
|
|
104
|
+
* @param distinctId which uniquely identifies your user
|
|
105
|
+
* @param properties with a dict with any key: value pairs
|
|
106
|
+
*/
|
|
107
|
+
identifyImmediate({ distinctId, properties }: IdentifyMessage): Promise<void>
|
|
108
|
+
|
|
91
109
|
/**
|
|
92
110
|
* @description To marry up whatever a user does before they sign up or log in with what they do after you need to make an alias call.
|
|
93
111
|
* This will allow you to answer questions like "Which marketing channels leads to users churning after a month?"
|
|
@@ -101,6 +119,14 @@ export type PostHogNodeV1 = {
|
|
|
101
119
|
*/
|
|
102
120
|
alias(data: { distinctId: string; alias: string }): void
|
|
103
121
|
|
|
122
|
+
/**
|
|
123
|
+
* @description To marry up whatever a user does before they sign up or log in with what they do after you need to make an alias call.
|
|
124
|
+
* Useful for edge environments where the usual queue-based sending is not preferable. Do not mix immediate and non-immediate calls.
|
|
125
|
+
* @param distinctId the current unique id
|
|
126
|
+
* @param alias the unique ID of the user before
|
|
127
|
+
*/
|
|
128
|
+
aliasImmediate(data: { distinctId: string; alias: string }): Promise<void>
|
|
129
|
+
|
|
104
130
|
/**
|
|
105
131
|
* @description PostHog feature flags (https://posthog.com/docs/features/feature-flags)
|
|
106
132
|
* allow you to safely deploy and roll back new features. Once you've created a feature flag in PostHog,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { MINIMUM_POLLING_INTERVAL, PostHog as PostHog, THIRTY_SECONDS } from '../src/posthog-node'
|
|
2
2
|
import fetch from '../src/fetch'
|
|
3
|
-
import { anyDecideCall, anyLocalEvalCall, apiImplementation } from './test-utils'
|
|
3
|
+
import { anyDecideCall, anyLocalEvalCall, apiImplementation, isPending } from './test-utils'
|
|
4
4
|
import { waitForPromises, wait } from '../../posthog-core/test/test-utils/test-utils'
|
|
5
5
|
import { randomUUID } from 'crypto'
|
|
6
6
|
jest.mock('../src/fetch')
|
|
@@ -381,33 +381,31 @@ describe('PostHog Node.js', () => {
|
|
|
381
381
|
})
|
|
382
382
|
ph.debug(true)
|
|
383
383
|
|
|
384
|
-
|
|
385
|
-
// which tells us when the flush is complete
|
|
384
|
+
ph.capture({ event: 'test-event-1', distinctId: '123' })
|
|
386
385
|
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
expect(
|
|
386
|
+
// start flushing, but don't wait for promise to resolve before resuming events
|
|
387
|
+
const flushPromise = ph.flush()
|
|
388
|
+
expect(isPending(flushPromise)).toEqual(true)
|
|
390
389
|
|
|
391
|
-
ph.capture({ event: 'test-event', distinctId: '123' })
|
|
392
|
-
ph.capture({ event: 'test-event', distinctId: '123' })
|
|
393
|
-
await wait(100)
|
|
394
|
-
expect(logSpy).toHaveBeenCalledTimes(3)
|
|
395
|
-
await wait(400) // The flush will resolve in this time
|
|
396
|
-
ph.capture({ event: 'test-event', distinctId: '123' })
|
|
397
|
-
ph.capture({ event: 'test-event', distinctId: '123' })
|
|
398
|
-
await wait(100)
|
|
399
|
-
expect(logSpy).toHaveBeenCalledTimes(6) // 5 captures and 1 flush
|
|
400
|
-
expect(5).toEqual(logSpy.mock.calls.filter((call) => call[1].includes('capture')).length)
|
|
401
|
-
expect(1).toEqual(logSpy.mock.calls.filter((call) => call[1].includes('flush')).length)
|
|
390
|
+
ph.capture({ event: 'test-event-2', distinctId: '123' })
|
|
402
391
|
|
|
403
|
-
|
|
404
|
-
|
|
392
|
+
// start shutdown, but don't wait for promise to resolve before resuming events
|
|
393
|
+
const shutdownPromise = ph.shutdown()
|
|
405
394
|
|
|
406
|
-
|
|
395
|
+
ph.capture({ event: 'test-event-3', distinctId: '123' })
|
|
396
|
+
|
|
397
|
+
// wait for shutdown to finish
|
|
398
|
+
await shutdownPromise
|
|
399
|
+
expect(isPending(flushPromise)).toEqual(false)
|
|
400
|
+
|
|
401
|
+
expect(3).toEqual(logSpy.mock.calls.filter((call) => call[1].includes('capture')).length)
|
|
402
|
+
const flushedEvents = logSpy.mock.calls.filter((call) => call[1].includes('flush')).flatMap((flush) => flush[2])
|
|
403
|
+
expect(flushedEvents).toMatchObject([
|
|
404
|
+
{ event: 'test-event-1' },
|
|
405
|
+
{ event: 'test-event-2' },
|
|
406
|
+
{ event: 'test-event-3' },
|
|
407
|
+
])
|
|
407
408
|
|
|
408
|
-
await ph.shutdown()
|
|
409
|
-
// 1 final flush for the events that were queued during shutdown
|
|
410
|
-
expect(1).toEqual(logSpy.mock.calls.filter((call) => call[1].includes('flush')).length)
|
|
411
409
|
logSpy.mockRestore()
|
|
412
410
|
warnSpy.mockRestore()
|
|
413
411
|
})
|
package/test/test-utils.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { PostHogV4DecideResponse } from 'posthog-core/src/types'
|
|
2
|
+
import util from 'util'
|
|
2
3
|
|
|
3
4
|
type ErrorResponse = {
|
|
4
5
|
status: number
|
|
@@ -104,3 +105,7 @@ export const anyLocalEvalCall = [
|
|
|
104
105
|
expect.any(Object),
|
|
105
106
|
]
|
|
106
107
|
export const anyDecideCall = ['http://example.com/flags/?v=2', expect.any(Object)]
|
|
108
|
+
|
|
109
|
+
export const isPending = (promise: Promise<any>): boolean => {
|
|
110
|
+
return util.inspect(promise).includes('pending')
|
|
111
|
+
}
|