posthog-node 1.3.0 → 2.0.0-alpha3
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/README.md +5 -20
- package/index.ts +4 -0
- package/lib/index.cjs.js +1300 -0
- package/lib/index.cjs.js.map +1 -0
- package/lib/index.d.ts +120 -0
- package/lib/index.esm.js +1291 -0
- package/lib/index.esm.js.map +1 -0
- package/lib/node_modules/tslib/tslib.es6.d.ts +35 -0
- package/lib/posthog-core/src/eventemitter.d.ts +8 -0
- package/lib/posthog-core/src/index.d.ts +84 -0
- package/lib/posthog-core/src/lz-string.d.ts +8 -0
- package/lib/posthog-core/src/types.d.ts +68 -0
- package/lib/posthog-core/src/utils.d.ts +13 -0
- package/lib/posthog-node/index.d.ts +3 -0
- package/lib/posthog-node/src/posthog-node.d.ts +22 -0
- package/lib/posthog-node/src/types.d.ts +83 -0
- package/package.json +35 -67
- package/src/posthog-node.ts +133 -0
- package/src/types.ts +99 -0
- package/test/posthog-node.spec.ts +92 -0
- package/tsconfig.json +6 -0
- package/LICENSE +0 -21
- package/cli.js +0 -72
- package/event-validation.js +0 -121
- package/feature-flags.js +0 -165
- package/index.d.ts +0 -106
- package/index.js +0 -362
package/package.json
CHANGED
|
@@ -1,69 +1,37 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
"
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
"
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
"
|
|
36
|
-
|
|
37
|
-
},
|
|
38
|
-
"keywords": [
|
|
39
|
-
"posthog",
|
|
40
|
-
"stats",
|
|
41
|
-
"analysis",
|
|
42
|
-
"funnels"
|
|
43
|
-
],
|
|
44
|
-
"dependencies": {
|
|
45
|
-
"axios": "0.24.0",
|
|
46
|
-
"axios-retry": "^3.1.9",
|
|
47
|
-
"component-type": "^1.2.1",
|
|
48
|
-
"join-component": "^1.1.0",
|
|
49
|
-
"md5": "^2.3.0",
|
|
50
|
-
"ms": "^2.1.3",
|
|
51
|
-
"remove-trailing-slash": "^0.1.1",
|
|
52
|
-
"uuid": "^8.3.2"
|
|
53
|
-
},
|
|
54
|
-
"devDependencies": {
|
|
55
|
-
"ava": "^3.15.0",
|
|
56
|
-
"basic-auth": "^2.0.1",
|
|
57
|
-
"body-parser": "^1.17.1",
|
|
58
|
-
"codecov": "^3.0.0",
|
|
59
|
-
"commander": "^2.9.0",
|
|
60
|
-
"delay": "^4.2.0",
|
|
61
|
-
"express": "^4.15.2",
|
|
62
|
-
"nyc": "^14.1.1",
|
|
63
|
-
"pify": "^4.0.1",
|
|
64
|
-
"prettier": "^2.3.1",
|
|
65
|
-
"sinon": "^7.3.2",
|
|
66
|
-
"size-limit": "^1.3.5",
|
|
67
|
-
"snyk": "^1.171.1"
|
|
68
|
-
}
|
|
2
|
+
"name": "posthog-node",
|
|
3
|
+
"version": "2.0.0-alpha3",
|
|
4
|
+
"description": "PostHog Node.js integration",
|
|
5
|
+
"repository": "PostHog/posthog-node",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"prepublish": "cd .. && yarn build"
|
|
8
|
+
},
|
|
9
|
+
"engines": {
|
|
10
|
+
"node": ">=10"
|
|
11
|
+
},
|
|
12
|
+
"license": "MIT",
|
|
13
|
+
"author": {
|
|
14
|
+
"name": "PostHog",
|
|
15
|
+
"email": "hey@posthog.com",
|
|
16
|
+
"url": "https://posthog.com"
|
|
17
|
+
},
|
|
18
|
+
"main": "lib/index.cjs.js",
|
|
19
|
+
"module": "lib/index.esm.js",
|
|
20
|
+
"types": "lib/index.d.ts",
|
|
21
|
+
"dependencies": {
|
|
22
|
+
"undici": "^5.8.0"
|
|
23
|
+
},
|
|
24
|
+
"devDependencies": {
|
|
25
|
+
"@types/node": "^18.0.0",
|
|
26
|
+
"commander": "^9.3.0"
|
|
27
|
+
},
|
|
28
|
+
"keywords": [
|
|
29
|
+
"posthog",
|
|
30
|
+
"stats",
|
|
31
|
+
"analysis",
|
|
32
|
+
"funnels"
|
|
33
|
+
],
|
|
34
|
+
"bin": {
|
|
35
|
+
"posthog": "cli.js"
|
|
36
|
+
}
|
|
69
37
|
}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import { version } from '../package.json'
|
|
2
|
+
import undici from 'undici'
|
|
3
|
+
import {
|
|
4
|
+
PostHogCore,
|
|
5
|
+
PosthogCoreOptions,
|
|
6
|
+
PostHogFetchOptions,
|
|
7
|
+
PostHogFetchResponse,
|
|
8
|
+
PostHogPersistedProperty,
|
|
9
|
+
} from '../../posthog-core/src'
|
|
10
|
+
import { EventMessageV1, GroupIdentifyMessage, IdentifyMessageV1, PostHogNodeV1 } from './types'
|
|
11
|
+
|
|
12
|
+
export type PostHogOptions = PosthogCoreOptions
|
|
13
|
+
|
|
14
|
+
class PostHog extends PostHogCore {
|
|
15
|
+
private _memoryStorage: { [key: string]: any | undefined } = {}
|
|
16
|
+
|
|
17
|
+
constructor(apiKey: string, options: PostHogOptions = {}) {
|
|
18
|
+
options.captureMode = options?.captureMode || 'json'
|
|
19
|
+
options.preloadFeatureFlags = false // Don't preload as this makes no sense without a distinctId
|
|
20
|
+
|
|
21
|
+
super(apiKey, options)
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
getPersistedProperty(key: PostHogPersistedProperty): any | undefined {
|
|
25
|
+
return this._memoryStorage[key]
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
setPersistedProperty(key: PostHogPersistedProperty, value: any | null): void {
|
|
29
|
+
this._memoryStorage[key] = value !== null ? value : undefined
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
getSessionId(): string | undefined {
|
|
33
|
+
// Sessions don't make sense for Node
|
|
34
|
+
return undefined
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
fetch(url: string, options: PostHogFetchOptions): Promise<PostHogFetchResponse> {
|
|
38
|
+
return undici.fetch(url, options)
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
getLibraryId(): string {
|
|
42
|
+
return 'posthog-node'
|
|
43
|
+
}
|
|
44
|
+
getLibraryVersion(): string {
|
|
45
|
+
return version
|
|
46
|
+
}
|
|
47
|
+
getCustomUserAgent(): string {
|
|
48
|
+
return `posthog-node/${version}`
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// The actual exported Nodejs API.
|
|
53
|
+
export class PostHogGlobal implements PostHogNodeV1 {
|
|
54
|
+
private _sharedClient: PostHog
|
|
55
|
+
|
|
56
|
+
constructor(apiKey: string, options: PostHogOptions = {}) {
|
|
57
|
+
options.decidePollInterval = 0 // Forcefully set to 0 so we don't auto-reload
|
|
58
|
+
this._sharedClient = new PostHog(apiKey, options)
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
private reInit(distinctId: string): void {
|
|
62
|
+
// Remove all state except the queue
|
|
63
|
+
const queue = this._sharedClient.getPersistedProperty(PostHogPersistedProperty.Queue)
|
|
64
|
+
this._sharedClient.reset()
|
|
65
|
+
this._sharedClient.setPersistedProperty(PostHogPersistedProperty.Queue, queue)
|
|
66
|
+
this._sharedClient.setPersistedProperty(PostHogPersistedProperty.DistinctId, distinctId)
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
enable(): void {
|
|
70
|
+
return this._sharedClient.optIn()
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
disable(): void {
|
|
74
|
+
return this._sharedClient.optOut()
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
capture({ distinctId, event, properties, groups }: EventMessageV1): void {
|
|
78
|
+
this.reInit(distinctId)
|
|
79
|
+
if (groups) {
|
|
80
|
+
this._sharedClient.groups(groups)
|
|
81
|
+
}
|
|
82
|
+
this._sharedClient.capture(event, properties)
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
identify({ distinctId, properties }: IdentifyMessageV1): void {
|
|
86
|
+
this.reInit(distinctId)
|
|
87
|
+
this._sharedClient.identify(distinctId, properties)
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
alias(data: { distinctId: string; alias: string }): void {
|
|
91
|
+
this.reInit(data.distinctId)
|
|
92
|
+
this._sharedClient.alias(data.alias)
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
async getFeatureFlag(
|
|
96
|
+
key: string,
|
|
97
|
+
distinctId: string,
|
|
98
|
+
groups?: Record<string, string> | undefined
|
|
99
|
+
): Promise<string | boolean | undefined> {
|
|
100
|
+
this.reInit(distinctId)
|
|
101
|
+
if (groups) {
|
|
102
|
+
this._sharedClient.groups(groups)
|
|
103
|
+
}
|
|
104
|
+
await this._sharedClient.reloadFeatureFlagsAsync()
|
|
105
|
+
return this._sharedClient.getFeatureFlag(key)
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
async isFeatureEnabled(
|
|
109
|
+
key: string,
|
|
110
|
+
distinctId: string,
|
|
111
|
+
defaultResult?: boolean | undefined,
|
|
112
|
+
groups?: Record<string, string> | undefined
|
|
113
|
+
): Promise<boolean> {
|
|
114
|
+
const feat = this.getFeatureFlag(key, distinctId, groups)
|
|
115
|
+
return !!feat || defaultResult || false
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
groupIdentify({ groupType, groupKey, properties }: GroupIdentifyMessage): void {
|
|
119
|
+
this._sharedClient.groupIdentify(groupType, groupKey, properties)
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
reloadFeatureFlags(): Promise<void> {
|
|
123
|
+
throw new Error('Method not implemented.')
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
shutdown(): void {
|
|
127
|
+
void this._sharedClient.shutdownAsync()
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
shutdownAsync(): Promise<void> {
|
|
131
|
+
return this._sharedClient.shutdownAsync()
|
|
132
|
+
}
|
|
133
|
+
}
|
package/src/types.ts
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
export interface IdentifyMessageV1 {
|
|
2
|
+
distinctId: string
|
|
3
|
+
properties?: Record<string | number, any>
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
export interface EventMessageV1 extends IdentifyMessageV1 {
|
|
7
|
+
event: string
|
|
8
|
+
groups?: Record<string, string | number> // Mapping of group type to group id
|
|
9
|
+
sendFeatureFlags?: boolean
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface GroupIdentifyMessage {
|
|
13
|
+
groupType: string
|
|
14
|
+
groupKey: string // Unique identifier for the group
|
|
15
|
+
properties?: Record<string | number, any>
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export type PostHogNodeV1 = {
|
|
19
|
+
/**
|
|
20
|
+
* @description Capture allows you to capture anything a user does within your system,
|
|
21
|
+
* which you can later use in PostHog to find patterns in usage,
|
|
22
|
+
* work out which features to improve or where people are giving up.
|
|
23
|
+
* A capture call requires:
|
|
24
|
+
* @param distinctId which uniquely identifies your user
|
|
25
|
+
* @param event We recommend using [verb] [noun], like movie played or movie updated to easily identify what your events mean later on.
|
|
26
|
+
* @param properties OPTIONAL | which can be a object with any information you'd like to add
|
|
27
|
+
* @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.
|
|
28
|
+
* @param sendFeatureFlags OPTIONAL | Used with experiments
|
|
29
|
+
*/
|
|
30
|
+
capture({ distinctId, event, properties, groups, sendFeatureFlags }: EventMessageV1): void
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* @description Identify lets you add metadata on your users so you can more easily identify who they are in PostHog,
|
|
34
|
+
* and even do things like segment users by these properties.
|
|
35
|
+
* An identify call requires:
|
|
36
|
+
* @param distinctId which uniquely identifies your user
|
|
37
|
+
* @param properties with a dict with any key: value pairs
|
|
38
|
+
*/
|
|
39
|
+
identify({ distinctId, properties }: IdentifyMessageV1): void
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* @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.
|
|
43
|
+
* This will allow you to answer questions like "Which marketing channels leads to users churning after a month?"
|
|
44
|
+
* or "What do users do on our website before signing up?"
|
|
45
|
+
* In a purely back-end implementation, this means whenever an anonymous user does something, you'll want to send a session ID with the capture call.
|
|
46
|
+
* Then, when that users signs up, you want to do an alias call with the session ID and the newly created user ID.
|
|
47
|
+
* The same concept applies for when a user logs in. If you're using PostHog in the front-end and back-end,
|
|
48
|
+
* doing the identify call in the frontend will be enough.:
|
|
49
|
+
* @param distinctId the current unique id
|
|
50
|
+
* @param alias the unique ID of the user before
|
|
51
|
+
*/
|
|
52
|
+
alias(data: { distinctId: string; alias: string }): void
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* @description PostHog feature flags (https://posthog.com/docs/features/feature-flags)
|
|
56
|
+
* allow you to safely deploy and roll back new features. Once you've created a feature flag in PostHog,
|
|
57
|
+
* you can use this method to check if the flag is on for a given user, allowing you to create logic to turn
|
|
58
|
+
* features on and off for different user groups or individual users.
|
|
59
|
+
* IMPORTANT: To use this method, you need to specify `personalApiKey` in your config! More info: https://posthog.com/docs/api/overview
|
|
60
|
+
* @param key the unique key of your feature flag
|
|
61
|
+
* @param distinctId the current unique id
|
|
62
|
+
* @param defaultResult optional - default value to be returned if the feature flag is not on for the user
|
|
63
|
+
* @param groups optional - what groups are currently active (group analytics)
|
|
64
|
+
*/
|
|
65
|
+
isFeatureEnabled(
|
|
66
|
+
key: string,
|
|
67
|
+
distinctId: string,
|
|
68
|
+
defaultResult?: boolean,
|
|
69
|
+
groups?: Record<string, string>
|
|
70
|
+
): Promise<boolean>
|
|
71
|
+
|
|
72
|
+
getFeatureFlag(
|
|
73
|
+
key: string,
|
|
74
|
+
distinctId: string,
|
|
75
|
+
groups?: Record<string, string>
|
|
76
|
+
): Promise<string | boolean | undefined>
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* @description Sets a groups properties, which allows asking questions like "Who are the most active companies"
|
|
80
|
+
* using my product in PostHog.
|
|
81
|
+
*
|
|
82
|
+
* @param groupType Type of group (ex: 'company'). Limited to 5 per project
|
|
83
|
+
* @param groupKey Unique identifier for that type of group (ex: 'id:5')
|
|
84
|
+
* @param properties OPTIONAL | which can be a object with any information you'd like to add
|
|
85
|
+
*/
|
|
86
|
+
groupIdentify({ groupType, groupKey, properties }: GroupIdentifyMessage): void
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* @description Force an immediate reload of the polled feature flags. Please note that they are
|
|
90
|
+
* already polled automatically at a regular interval.
|
|
91
|
+
*/
|
|
92
|
+
reloadFeatureFlags(): Promise<void>
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* @description Flushes the events still in the queue and clears the feature flags poller to allow for
|
|
96
|
+
* a clean shutdown.
|
|
97
|
+
*/
|
|
98
|
+
shutdown(): void
|
|
99
|
+
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import PostHog from '../'
|
|
2
|
+
jest.mock('undici')
|
|
3
|
+
import undici from 'undici'
|
|
4
|
+
|
|
5
|
+
const mockedUndici = jest.mocked(undici, true)
|
|
6
|
+
|
|
7
|
+
const getLastBatchEvents = (): any[] | undefined => {
|
|
8
|
+
expect(mockedUndici.fetch).toHaveBeenCalledWith(
|
|
9
|
+
'http://example.com/batch/',
|
|
10
|
+
expect.objectContaining({ method: 'POST' })
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
const call = mockedUndici.fetch.mock.calls.find((x) => (x[0] as string).includes('/batch/'))
|
|
14
|
+
if (!call) {
|
|
15
|
+
return undefined
|
|
16
|
+
}
|
|
17
|
+
return JSON.parse((call[1] as any).body as any).batch
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
describe('PostHog Core', () => {
|
|
21
|
+
let posthog: PostHog
|
|
22
|
+
|
|
23
|
+
jest.useFakeTimers()
|
|
24
|
+
|
|
25
|
+
beforeEach(() => {
|
|
26
|
+
posthog = new PostHog('TEST_API_KEY', {
|
|
27
|
+
host: 'http://example.com',
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
mockedUndici.fetch.mockResolvedValue({
|
|
31
|
+
status: 200,
|
|
32
|
+
text: () => Promise.resolve('ok'),
|
|
33
|
+
json: () =>
|
|
34
|
+
Promise.resolve({
|
|
35
|
+
status: 'ok',
|
|
36
|
+
}),
|
|
37
|
+
} as any)
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
describe('legacy methods', () => {
|
|
41
|
+
it('should capture an event to shared queue', async () => {
|
|
42
|
+
expect(mockedUndici.fetch).toHaveBeenCalledTimes(0)
|
|
43
|
+
posthog.capture({ distinctId: '123', event: 'test-event', properties: { foo: 'bar' }, groups: { org: 123 } })
|
|
44
|
+
|
|
45
|
+
jest.runOnlyPendingTimers()
|
|
46
|
+
const batchEvents = getLastBatchEvents()
|
|
47
|
+
expect(batchEvents).toMatchObject([
|
|
48
|
+
{
|
|
49
|
+
distinct_id: '123',
|
|
50
|
+
event: 'test-event',
|
|
51
|
+
properties: {
|
|
52
|
+
$groups: { org: 123 },
|
|
53
|
+
foo: 'bar',
|
|
54
|
+
},
|
|
55
|
+
},
|
|
56
|
+
])
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
it('should capture identify events on shared queue', async () => {
|
|
60
|
+
expect(mockedUndici.fetch).toHaveBeenCalledTimes(0)
|
|
61
|
+
posthog.identify({ distinctId: '123', properties: { foo: 'bar' } })
|
|
62
|
+
jest.runOnlyPendingTimers()
|
|
63
|
+
const batchEvents = getLastBatchEvents()
|
|
64
|
+
expect(batchEvents).toMatchObject([
|
|
65
|
+
{
|
|
66
|
+
distinct_id: '123',
|
|
67
|
+
event: '$identify',
|
|
68
|
+
properties: {
|
|
69
|
+
foo: 'bar',
|
|
70
|
+
},
|
|
71
|
+
},
|
|
72
|
+
])
|
|
73
|
+
})
|
|
74
|
+
|
|
75
|
+
it('should capture alias events on shared queue', async () => {
|
|
76
|
+
expect(mockedUndici.fetch).toHaveBeenCalledTimes(0)
|
|
77
|
+
posthog.alias({ distinctId: '123', alias: '1234' })
|
|
78
|
+
jest.runOnlyPendingTimers()
|
|
79
|
+
const batchEvents = getLastBatchEvents()
|
|
80
|
+
expect(batchEvents).toMatchObject([
|
|
81
|
+
{
|
|
82
|
+
distinct_id: '123',
|
|
83
|
+
event: '$create_alias',
|
|
84
|
+
properties: {
|
|
85
|
+
distinct_id: '123',
|
|
86
|
+
alias: '1234',
|
|
87
|
+
},
|
|
88
|
+
},
|
|
89
|
+
])
|
|
90
|
+
})
|
|
91
|
+
})
|
|
92
|
+
})
|
package/tsconfig.json
ADDED
package/LICENSE
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
Copyright (c) 2020 PostHog (part of Hiberly Inc)
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2013 Segment Inc. friends@segment.com
|
|
4
|
-
|
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
-
in the Software without restriction, including without limitation the rights
|
|
8
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
-
furnished to do so, subject to the following conditions:
|
|
11
|
-
|
|
12
|
-
The above copyright notice and this permission notice shall be included in all
|
|
13
|
-
copies or substantial portions of the Software.
|
|
14
|
-
|
|
15
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
-
SOFTWARE.
|
package/cli.js
DELETED
|
@@ -1,72 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
'use strict'
|
|
3
|
-
|
|
4
|
-
const program = require('commander')
|
|
5
|
-
const PostHog = require('.')
|
|
6
|
-
const pkg = require('./package')
|
|
7
|
-
|
|
8
|
-
const toObject = (str) => JSON.parse(str)
|
|
9
|
-
|
|
10
|
-
program
|
|
11
|
-
.version(pkg.version)
|
|
12
|
-
.option('-k, --apiKey <key>', 'the PostHog api key to use')
|
|
13
|
-
.option('-h, --host <host>', 'the PostHog API hostname to use')
|
|
14
|
-
.option('-t, --type <type>', 'the PostHog message type')
|
|
15
|
-
|
|
16
|
-
.option('-d, --distinctId <id>', 'the distinct id to send the event as')
|
|
17
|
-
|
|
18
|
-
.option('-e, --event <event>', 'the event name to send with the event')
|
|
19
|
-
.option('-p, --properties <properties>', 'the event properties to send (JSON-encoded)', toObject)
|
|
20
|
-
|
|
21
|
-
.option('-a, --alias <previousId>', 'alias for the distinct id')
|
|
22
|
-
|
|
23
|
-
.parse(process.argv)
|
|
24
|
-
|
|
25
|
-
if (program.args.length !== 0) {
|
|
26
|
-
program.help()
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
const apiKey = program.apiKey
|
|
30
|
-
const host = program.host
|
|
31
|
-
const type = program.type
|
|
32
|
-
|
|
33
|
-
const distinctId = program.distinctId
|
|
34
|
-
|
|
35
|
-
const event = program.event
|
|
36
|
-
const properties = program.properties
|
|
37
|
-
const alias = program.alias
|
|
38
|
-
|
|
39
|
-
const run = (method, args) => {
|
|
40
|
-
const posthog = new PostHog(apiKey, { host, flushAt: 1 })
|
|
41
|
-
posthog[method](args, (err) => {
|
|
42
|
-
if (err) {
|
|
43
|
-
console.error(err.stack)
|
|
44
|
-
process.exit(1)
|
|
45
|
-
}
|
|
46
|
-
})
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
switch (type) {
|
|
50
|
-
case 'capture':
|
|
51
|
-
run('capture', {
|
|
52
|
-
event,
|
|
53
|
-
properties,
|
|
54
|
-
distinctId,
|
|
55
|
-
})
|
|
56
|
-
break
|
|
57
|
-
case 'identify':
|
|
58
|
-
run('identify', {
|
|
59
|
-
properties,
|
|
60
|
-
distinctId,
|
|
61
|
-
})
|
|
62
|
-
break
|
|
63
|
-
case 'alias':
|
|
64
|
-
run('alias', {
|
|
65
|
-
alias,
|
|
66
|
-
distinctId,
|
|
67
|
-
})
|
|
68
|
-
break
|
|
69
|
-
default:
|
|
70
|
-
console.error('invalid type:', type)
|
|
71
|
-
process.exit(1)
|
|
72
|
-
}
|
package/event-validation.js
DELETED
|
@@ -1,121 +0,0 @@
|
|
|
1
|
-
var type = require('component-type')
|
|
2
|
-
var join = require('join-component')
|
|
3
|
-
var assert = require('assert')
|
|
4
|
-
|
|
5
|
-
// PostHog messages can be a maximum of 32 kB.
|
|
6
|
-
var MAX_SIZE = 32 << 10
|
|
7
|
-
|
|
8
|
-
module.exports = eventValidation
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Validate an event.
|
|
12
|
-
*/
|
|
13
|
-
|
|
14
|
-
function eventValidation(event, type) {
|
|
15
|
-
validateGenericEvent(event)
|
|
16
|
-
type = type || event.type
|
|
17
|
-
assert(type, 'You must pass an event type.')
|
|
18
|
-
switch (type) {
|
|
19
|
-
case 'capture':
|
|
20
|
-
return validateCaptureEvent(event)
|
|
21
|
-
case 'identify':
|
|
22
|
-
return validateIdentifyEvent(event)
|
|
23
|
-
case 'alias':
|
|
24
|
-
return validateAliasEvent(event)
|
|
25
|
-
case 'groupIdentify':
|
|
26
|
-
return validateGroupIdentifyEvent(event)
|
|
27
|
-
case 'isFeatureEnabled':
|
|
28
|
-
return validateIsFeatureEnabled(event)
|
|
29
|
-
default:
|
|
30
|
-
assert(0, 'Invalid event type: "' + type + '"')
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* Validate a "capture" event.
|
|
36
|
-
*/
|
|
37
|
-
|
|
38
|
-
function validateCaptureEvent(event) {
|
|
39
|
-
assert(event.distinctId, 'You must pass a "distinctId".')
|
|
40
|
-
assert(event.event, 'You must pass an "event".')
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
/**
|
|
44
|
-
* Validate a "identify" event.
|
|
45
|
-
*/
|
|
46
|
-
|
|
47
|
-
function validateIdentifyEvent(event) {
|
|
48
|
-
assert(event.distinctId, 'You must pass a "distinctId".')
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
/**
|
|
52
|
-
* Validate an "alias" event.
|
|
53
|
-
*/
|
|
54
|
-
|
|
55
|
-
function validateAliasEvent(event) {
|
|
56
|
-
assert(event.distinctId, 'You must pass a "distinctId".')
|
|
57
|
-
assert(event.alias, 'You must pass a "alias".')
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
/**
|
|
61
|
-
* Validate an "groupIdentify" event.
|
|
62
|
-
*/
|
|
63
|
-
|
|
64
|
-
function validateGroupIdentifyEvent(event) {
|
|
65
|
-
assert(event.groupType, 'You must pass a "groupType".')
|
|
66
|
-
assert(event.groupKey, 'You must pass a "groupKey".')
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
/**
|
|
70
|
-
* Validate a "isFeatureEnabled" call
|
|
71
|
-
*/
|
|
72
|
-
|
|
73
|
-
function validateIsFeatureEnabled(event) {
|
|
74
|
-
assert(event.key, 'You must pass a "key".')
|
|
75
|
-
assert(event.distinctId, 'You must pass a "distinctId".')
|
|
76
|
-
assert(type(event.defaultResult) == 'boolean', '"defaultResult" must be a boolean.')
|
|
77
|
-
if (event.groups) {
|
|
78
|
-
assert(type(event.groups) == 'object', 'You must pass an object for "groups".')
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
/**
|
|
84
|
-
* Validation rules.
|
|
85
|
-
*/
|
|
86
|
-
|
|
87
|
-
var genericValidationRules = {
|
|
88
|
-
event: 'string',
|
|
89
|
-
properties: 'object',
|
|
90
|
-
alias: 'string',
|
|
91
|
-
timestamp: 'date',
|
|
92
|
-
distinctId: 'string',
|
|
93
|
-
type: 'string',
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
/**
|
|
97
|
-
* Validate an event object.
|
|
98
|
-
*/
|
|
99
|
-
|
|
100
|
-
function validateGenericEvent(event) {
|
|
101
|
-
assert(type(event) === 'object', 'You must pass a message object.')
|
|
102
|
-
var json = JSON.stringify(event)
|
|
103
|
-
// Strings are variable byte encoded, so json.length is not sufficient.
|
|
104
|
-
assert(Buffer.byteLength(json, 'utf8') < MAX_SIZE, 'Your message must be < 32 kB.')
|
|
105
|
-
|
|
106
|
-
for (var key in genericValidationRules) {
|
|
107
|
-
var val = event[key]
|
|
108
|
-
if (!val) continue
|
|
109
|
-
var rule = genericValidationRules[key]
|
|
110
|
-
if (type(rule) !== 'array') {
|
|
111
|
-
rule = [rule]
|
|
112
|
-
}
|
|
113
|
-
var a = rule[0] === 'object' ? 'an' : 'a'
|
|
114
|
-
assert(
|
|
115
|
-
rule.some(function (e) {
|
|
116
|
-
return type(val) === e
|
|
117
|
-
}),
|
|
118
|
-
'"' + key + '" must be ' + a + ' ' + join(rule, 'or') + '.'
|
|
119
|
-
)
|
|
120
|
-
}
|
|
121
|
-
}
|