posthog-node
Advanced tools
Comparing version 4.0.0-beta.1 to 4.0.0-beta.2
@@ -0,1 +1,7 @@ | ||
# 4.0.0-beta.2 - 2024-03-12 | ||
- `flushAsync` and `shutdownAsync` are removed with `flush` and `shutdown` now being the async methods. | ||
- Fixed an issue where `shutdown` would potentially exit early if a flush was already in progress | ||
- Flushes will now try to flush up to `maxBatchSize` (default 100) in one go | ||
# 4.0.0-beta.1 - 2024-03-04 | ||
@@ -2,0 +8,0 @@ |
@@ -9,2 +9,4 @@ /// <reference types="node" /> | ||
flushInterval?: number; | ||
/** The maximum number of queued messages to be flushed as part of a single batch (must be higher than `flushAt`) */ | ||
maxBatchSize?: number; | ||
/** If set to true the SDK is essentially disabled (useful for local environments where you don't want to track anything) */ | ||
@@ -126,3 +128,5 @@ disabled?: boolean; | ||
private flushAt; | ||
private maxBatchSize; | ||
private flushInterval; | ||
private flushPromise; | ||
private requestTimeout; | ||
@@ -157,3 +161,3 @@ private featureFlagsRequestTimeoutMs; | ||
private buildPayload; | ||
protected addPendingPromise(promise: Promise<any>): void; | ||
protected addPendingPromise<T>(promise: Promise<T>): Promise<T>; | ||
/*** | ||
@@ -190,7 +194,12 @@ *** TRACKING | ||
protected enqueue(type: string, _message: any, options?: PostHogCaptureOptions): void; | ||
flushAsync(): Promise<any>; | ||
flush(callback?: (err?: any, data?: any) => void): void; | ||
private clearFlushTimer; | ||
/** | ||
* Helper for flushing the queue in the background | ||
* Avoids unnecessary promise errors | ||
*/ | ||
private flushBackground; | ||
flush(): Promise<any[]>; | ||
private _flush; | ||
private fetchWithRetry; | ||
shutdownAsync(shutdownTimeoutMs?: number): Promise<void>; | ||
shutdown(shutdownTimeoutMs?: number): void; | ||
shutdown(shutdownTimeoutMs?: number): Promise<void>; | ||
} | ||
@@ -409,4 +418,3 @@ | ||
reloadFeatureFlags(): Promise<void>; | ||
shutdown(shutdownTimeoutMs?: number): void; | ||
shutdownAsync(shutdownTimeoutMs?: number): Promise<void>; | ||
shutdown(shutdownTimeoutMs?: number): Promise<void>; | ||
private addLocalPersonAndGroupProperties; | ||
@@ -413,0 +421,0 @@ } |
@@ -10,3 +10,5 @@ import { PostHogFetchOptions, PostHogFetchResponse, PostHogAutocaptureElement, PostHogDecideResponse, PostHogCoreOptions, PostHogEventProperties, PostHogPersistedProperty, PostHogCaptureOptions, JsonType } from './types'; | ||
private flushAt; | ||
private maxBatchSize; | ||
private flushInterval; | ||
private flushPromise; | ||
private requestTimeout; | ||
@@ -41,3 +43,3 @@ private featureFlagsRequestTimeoutMs; | ||
private buildPayload; | ||
protected addPendingPromise(promise: Promise<any>): void; | ||
protected addPendingPromise<T>(promise: Promise<T>): Promise<T>; | ||
/*** | ||
@@ -74,7 +76,12 @@ *** TRACKING | ||
protected enqueue(type: string, _message: any, options?: PostHogCaptureOptions): void; | ||
flushAsync(): Promise<any>; | ||
flush(callback?: (err?: any, data?: any) => void): void; | ||
private clearFlushTimer; | ||
/** | ||
* Helper for flushing the queue in the background | ||
* Avoids unnecessary promise errors | ||
*/ | ||
private flushBackground; | ||
flush(): Promise<any[]>; | ||
private _flush; | ||
private fetchWithRetry; | ||
shutdownAsync(shutdownTimeoutMs?: number): Promise<void>; | ||
shutdown(shutdownTimeoutMs?: number): void; | ||
shutdown(shutdownTimeoutMs?: number): Promise<void>; | ||
} | ||
@@ -81,0 +88,0 @@ export declare abstract class PostHogCore extends PostHogCoreStateless { |
@@ -9,2 +9,4 @@ /// <reference types="node" /> | ||
flushInterval?: number; | ||
/** The maximum number of queued messages to be flushed as part of a single batch (must be higher than `flushAt`) */ | ||
maxBatchSize?: number; | ||
/** If set to true the SDK is essentially disabled (useful for local environments where you don't want to track anything) */ | ||
@@ -11,0 +13,0 @@ disabled?: boolean; |
@@ -73,5 +73,4 @@ import { JsonType, PostHogCoreOptions, PostHogCoreStateless, PostHogFetchOptions, PostHogFetchResponse, PostHogFlagsAndPayloadsResponse, PostHogPersistedProperty } from '../../posthog-core/src'; | ||
reloadFeatureFlags(): Promise<void>; | ||
shutdown(shutdownTimeoutMs?: number): void; | ||
shutdownAsync(shutdownTimeoutMs?: number): Promise<void>; | ||
shutdown(shutdownTimeoutMs?: number): Promise<void>; | ||
private addLocalPersonAndGroupProperties; | ||
} |
{ | ||
"name": "posthog-node", | ||
"version": "4.0.0-beta.1", | ||
"version": "4.0.0-beta.2", | ||
"description": "PostHog Node.js integration", | ||
@@ -5,0 +5,0 @@ "repository": { |
@@ -456,9 +456,5 @@ import { version } from '../package.json' | ||
shutdown(shutdownTimeoutMs?: number): void { | ||
void this.shutdownAsync(shutdownTimeoutMs) | ||
} | ||
async shutdownAsync(shutdownTimeoutMs?: number): Promise<void> { | ||
async shutdown(shutdownTimeoutMs?: number): Promise<void> { | ||
this.featureFlagsPoller?.stopPoller() | ||
return super.shutdownAsync(shutdownTimeoutMs) | ||
return super.shutdown(shutdownTimeoutMs) | ||
} | ||
@@ -465,0 +461,0 @@ |
@@ -89,3 +89,3 @@ // import { PostHog } from '../' | ||
// ensure clean shutdown & no test interdependencies | ||
await posthog.shutdownAsync() | ||
await posthog.shutdown() | ||
}) | ||
@@ -113,4 +113,5 @@ | ||
await waitForPromises() | ||
jest.runOnlyPendingTimers() | ||
await waitForPromises() // First flush | ||
jest.runOnlyPendingTimers() // Flush timer | ||
await waitForPromises() // Second flush | ||
const batchEvents = getLastBatchEvents() | ||
@@ -117,0 +118,0 @@ |
@@ -1,2 +0,1 @@ | ||
// import { PostHog } from '../' | ||
import { PostHog as PostHog } from '../src/posthog-node' | ||
@@ -13,2 +12,10 @@ jest.mock('../src/fetch') | ||
const waitForFlushTimer = async (): Promise<void> => { | ||
await waitForPromises() | ||
// To trigger the flush via the timer | ||
jest.runOnlyPendingTimers() | ||
// Then wait for the flush promise | ||
await waitForPromises() | ||
} | ||
const getLastBatchEvents = (): any[] | undefined => { | ||
@@ -47,4 +54,13 @@ expect(mockedFetch).toHaveBeenCalledWith('http://example.com/batch/', expect.objectContaining({ method: 'POST' })) | ||
afterEach(async () => { | ||
mockedFetch.mockResolvedValue({ | ||
status: 200, | ||
text: () => Promise.resolve('ok'), | ||
json: () => | ||
Promise.resolve({ | ||
status: 'ok', | ||
}), | ||
} as any) | ||
// ensure clean shutdown & no test interdependencies | ||
await posthog.shutdownAsync() | ||
await posthog.shutdown() | ||
}) | ||
@@ -57,4 +73,3 @@ | ||
await waitForPromises() | ||
jest.runOnlyPendingTimers() | ||
await waitForFlushTimer() | ||
@@ -86,4 +101,3 @@ const batchEvents = getLastBatchEvents() | ||
await waitForPromises() | ||
jest.runOnlyPendingTimers() | ||
await waitForFlushTimer() | ||
expect(getLastBatchEvents()?.[0]).toEqual( | ||
@@ -110,4 +124,3 @@ expect.objectContaining({ | ||
await waitForPromises() | ||
jest.runOnlyPendingTimers() | ||
await waitForFlushTimer() | ||
expect(getLastBatchEvents()?.[0]).toEqual( | ||
@@ -132,2 +145,4 @@ expect.objectContaining({ | ||
jest.runOnlyPendingTimers() | ||
await waitForPromises() | ||
const batchEvents = getLastBatchEvents() | ||
@@ -152,2 +167,3 @@ expect(batchEvents).toMatchObject([ | ||
jest.runOnlyPendingTimers() | ||
await waitForPromises() | ||
const batchEvents = getLastBatchEvents() | ||
@@ -172,2 +188,3 @@ expect(batchEvents).toMatchObject([ | ||
jest.runOnlyPendingTimers() | ||
await waitForPromises() | ||
const batchEvents = getLastBatchEvents() | ||
@@ -190,4 +207,3 @@ expect(batchEvents).toMatchObject([ | ||
posthog.capture({ event: 'custom-time', distinctId: '123', timestamp: new Date('2021-02-03') }) | ||
await waitForPromises() | ||
jest.runOnlyPendingTimers() | ||
await waitForFlushTimer() | ||
const batchEvents = getLastBatchEvents() | ||
@@ -208,4 +224,3 @@ expect(batchEvents).toMatchObject([ | ||
posthog.capture({ event: 'custom-time', distinctId: '123', uuid }) | ||
await waitForPromises() | ||
jest.runOnlyPendingTimers() | ||
await waitForFlushTimer() | ||
const batchEvents = getLastBatchEvents() | ||
@@ -232,4 +247,3 @@ expect(batchEvents).toMatchObject([ | ||
await waitForPromises() | ||
jest.runOnlyPendingTimers() | ||
await waitForFlushTimer() | ||
const batchEvents = getLastBatchEvents() | ||
@@ -252,4 +266,3 @@ expect(batchEvents?.[0].properties).toEqual({ | ||
await waitForPromises() | ||
jest.runOnlyPendingTimers() | ||
await waitForFlushTimer() | ||
@@ -272,4 +285,4 @@ let batchEvents = getLastBatchEvents() | ||
await waitForPromises() | ||
jest.runOnlyPendingTimers() | ||
await waitForFlushTimer() | ||
batchEvents = getLastBatchEvents() | ||
@@ -292,4 +305,5 @@ expect(batchEvents?.[0].properties).toEqual({ | ||
await waitForFlushTimer() | ||
await waitForPromises() | ||
jest.runOnlyPendingTimers() | ||
batchEvents = getLastBatchEvents() | ||
@@ -303,3 +317,3 @@ expect(batchEvents?.[0].properties).toEqual({ | ||
await client.shutdownAsync() | ||
await client.shutdown() | ||
}) | ||
@@ -309,8 +323,11 @@ }) | ||
describe('shutdown', () => { | ||
let warnSpy: jest.SpyInstance, logSpy: jest.SpyInstance | ||
beforeEach(() => { | ||
// a serverless posthog configuration | ||
posthog = new PostHog('TEST_API_KEY', { | ||
host: 'http://example.com', | ||
fetchRetryCount: 0, | ||
const actualLog = console.log | ||
warnSpy = jest.spyOn(console, 'warn').mockImplementation((...args) => { | ||
actualLog('spied warn:', ...args) | ||
}) | ||
logSpy = jest.spyOn(console, 'log').mockImplementation((...args) => { | ||
actualLog('spied log:', ...args) | ||
}) | ||
@@ -330,6 +347,7 @@ mockedFetch.mockImplementation(async () => { | ||
}) | ||
jest.useRealTimers() | ||
}) | ||
afterEach(() => { | ||
posthog.debug(false) | ||
jest.useFakeTimers() | ||
@@ -339,3 +357,3 @@ }) | ||
it('should shutdown cleanly', async () => { | ||
posthog = new PostHog('TEST_API_KEY', { | ||
const ph = new PostHog('TEST_API_KEY', { | ||
host: 'http://example.com', | ||
@@ -345,32 +363,37 @@ fetchRetryCount: 0, | ||
}) | ||
ph.debug(true) | ||
const logSpy = jest.spyOn(console, 'log').mockImplementation(() => {}) | ||
jest.useRealTimers() | ||
// using debug mode to check console.log output | ||
// which tells us when the flush is complete | ||
posthog.debug(true) | ||
for (let i = 0; i < 10; i++) { | ||
posthog.capture({ event: 'test-event', distinctId: '123' }) | ||
// requests come 100ms apart | ||
await wait(100) | ||
} | ||
// 10 capture calls to debug log | ||
// 6 flush calls to debug log | ||
expect(logSpy).toHaveBeenCalledTimes(16) | ||
expect(10).toEqual(logSpy.mock.calls.filter((call) => call[1].includes('capture')).length) | ||
expect(6).toEqual(logSpy.mock.calls.filter((call) => call[1].includes('flush')).length) | ||
ph.capture({ event: 'test-event', distinctId: '123' }) | ||
await wait(100) | ||
expect(logSpy).toHaveBeenCalledTimes(1) | ||
ph.capture({ event: 'test-event', distinctId: '123' }) | ||
ph.capture({ event: 'test-event', distinctId: '123' }) | ||
await wait(100) | ||
expect(logSpy).toHaveBeenCalledTimes(3) | ||
await wait(400) // The flush will resolve in this time | ||
ph.capture({ event: 'test-event', distinctId: '123' }) | ||
ph.capture({ event: 'test-event', distinctId: '123' }) | ||
await wait(100) | ||
expect(logSpy).toHaveBeenCalledTimes(6) // 5 captures and 1 flush | ||
expect(5).toEqual(logSpy.mock.calls.filter((call) => call[1].includes('capture')).length) | ||
expect(1).toEqual(logSpy.mock.calls.filter((call) => call[1].includes('flush')).length) | ||
logSpy.mockClear() | ||
expect(logSpy).toHaveBeenCalledTimes(0) | ||
await posthog.shutdownAsync() | ||
// remaining 4 flush calls to debug log | ||
// happen during shutdown | ||
expect(4).toEqual(logSpy.mock.calls.filter((call) => call[1].includes('flush')).length) | ||
jest.useFakeTimers() | ||
console.warn('YOO!!') | ||
await ph.shutdown() | ||
// 1 final flush for the events that were queued during shutdown | ||
expect(1).toEqual(logSpy.mock.calls.filter((call) => call[1].includes('flush')).length) | ||
logSpy.mockRestore() | ||
warnSpy.mockRestore() | ||
}) | ||
it('should shutdown cleanly with pending capture flag promises', async () => { | ||
posthog = new PostHog('TEST_API_KEY', { | ||
const ph = new PostHog('TEST_API_KEY', { | ||
host: 'http://example.com', | ||
@@ -380,15 +403,12 @@ fetchRetryCount: 0, | ||
}) | ||
ph.debug(true) | ||
const logSpy = jest.spyOn(console, 'log').mockImplementation(() => {}) | ||
jest.useRealTimers() | ||
posthog.debug(true) | ||
for (let i = 0; i < 10; i++) { | ||
posthog.capture({ event: 'test-event', distinctId: `${i}`, sendFeatureFlags: true }) | ||
ph.capture({ event: 'test-event', distinctId: `${i}`, sendFeatureFlags: true }) | ||
} | ||
await posthog.shutdownAsync() | ||
await ph.shutdown() | ||
// all capture calls happen during shutdown | ||
const batchEvents = getLastBatchEvents() | ||
expect(batchEvents?.length).toEqual(2) | ||
expect(batchEvents?.length).toEqual(6) | ||
expect(batchEvents?.[batchEvents?.length - 1]).toMatchObject({ | ||
@@ -409,4 +429,4 @@ // last event in batch | ||
expect(10).toEqual(logSpy.mock.calls.filter((call) => call[1].includes('capture')).length) | ||
expect(3).toEqual(logSpy.mock.calls.filter((call) => call[1].includes('flush')).length) | ||
jest.useFakeTimers() | ||
// 1 for the captured events, 1 for the final flush of feature flag called events | ||
expect(2).toEqual(logSpy.mock.calls.filter((call) => call[1].includes('flush')).length) | ||
logSpy.mockRestore() | ||
@@ -420,3 +440,3 @@ }) | ||
jest.runOnlyPendingTimers() | ||
await posthog.flushAsync() | ||
await posthog.flush() | ||
const batchEvents = getLastBatchEvents() | ||
@@ -446,3 +466,3 @@ expect(batchEvents).toMatchObject([ | ||
jest.runOnlyPendingTimers() | ||
await posthog.flushAsync() | ||
await posthog.flush() | ||
const batchEvents = getLastBatchEvents() | ||
@@ -681,3 +701,3 @@ expect(batchEvents).toMatchObject([ | ||
await posthog.shutdownAsync() | ||
await posthog.shutdown() | ||
}) | ||
@@ -755,4 +775,3 @@ | ||
await waitForPromises() | ||
jest.runOnlyPendingTimers() | ||
await waitForFlushTimer() | ||
@@ -885,3 +904,3 @@ expect(mockedFetch).toHaveBeenCalledWith( | ||
await waitForPromises() | ||
await posthog.flushAsync() | ||
await posthog.flush() | ||
@@ -915,3 +934,3 @@ expect(mockedFetch).toHaveBeenCalledWith('http://example.com/batch/', expect.any(Object)) | ||
await waitForPromises() | ||
await posthog.flushAsync() | ||
await posthog.flush() | ||
@@ -930,3 +949,3 @@ expect(mockedFetch).not.toHaveBeenCalledWith('http://example.com/batch/', expect.any(Object)) | ||
await waitForPromises() | ||
await posthog.flushAsync() | ||
await posthog.flush() | ||
expect(mockedFetch).toHaveBeenCalledWith('http://example.com/batch/', expect.any(Object)) | ||
@@ -960,3 +979,3 @@ | ||
await waitForPromises() | ||
await posthog.flushAsync() | ||
await posthog.flush() | ||
expect(mockedFetch).not.toHaveBeenCalledWith('http://example.com/batch/', expect.any(Object)) | ||
@@ -973,3 +992,3 @@ | ||
await waitForPromises() | ||
await posthog.flushAsync() | ||
await posthog.flush() | ||
// one to decide, one to batch | ||
@@ -1004,3 +1023,3 @@ expect(mockedFetch).toHaveBeenCalledWith(...anyDecideCall) | ||
await waitForPromises() | ||
await posthog.flushAsync() | ||
await posthog.flush() | ||
// call decide, but not batch | ||
@@ -1007,0 +1026,0 @@ expect(mockedFetch).toHaveBeenCalledWith(...anyDecideCall) |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
868295
12774