Skip to content

Commit 7108166

Browse files
committed
feat: analytics engine and providers
1 parent 5c6dccb commit 7108166

23 files changed

+549
-15
lines changed

.github/workflows/publish-dev.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ jobs:
5858
"@commandkit/i18n:packages/i18n"
5959
"@commandkit/devtools:packages/devtools"
6060
"@commandkit/cache:packages/cache"
61+
"@commandkit/analytics:packages/analytics"
6162
)
6263
6364
for entry in "${PACKAGES[@]}"; do
@@ -78,6 +79,7 @@ jobs:
7879
"@commandkit/i18n"
7980
"@commandkit/devtools"
8081
"@commandkit/cache"
82+
"@commandkit/analytics"
8183
)
8284
8385
for pkg in "${PACKAGES[@]}"; do

packages/analytics/README.md

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
# `@commandkit/analytics`
2+
3+
Analytics providers for CommandKit.
4+
5+
## Installation
6+
7+
```sh
8+
npm install @commandkit/analytics
9+
```
10+
11+
## Usage
12+
13+
This package provides a commandkit plugin that automatically registers the analytics provider with the commandkit instance.
14+
15+
### PostHog
16+
17+
```js
18+
import { posthog } from '@commandkit/analytics/posthog';
19+
20+
export default defineConfig({
21+
plugins: [
22+
posthog({
23+
posthogOptions: {
24+
apiKey: 'YOUR_POSTHOG_API_KEY',
25+
options?: PostHogOptions
26+
}
27+
})
28+
],
29+
});
30+
```
31+
32+
### Umami
33+
34+
```js
35+
import { umami } from '@commandkit/analytics/umami';
36+
37+
export default defineConfig({
38+
plugins: [
39+
umami({
40+
umamiOptions: {
41+
hostUrl: 'YOUR_UMAMI_HOST_URL',
42+
websiteId?: 'YOUR_UMAMI_WEBSITE_ID',
43+
sessionId?: 'YOUR_UMAMI_SESSION_ID',
44+
userAgent?: 'YOUR_UMAMI_USER_AGENT',
45+
}
46+
})
47+
],
48+
});
49+
```
50+
51+
### Tracking events
52+
53+
```js
54+
import { track } from 'commandkit/analytics';
55+
56+
await track({
57+
name: 'YOUR_EVENT_NAME',
58+
id?: 'YOUR_UNIQUE_ID',
59+
data: {...}
60+
});
61+
```
62+
63+
### Disabling analytics per-request scope
64+
65+
The `noAnalytics` function can be used to disable analytics for a specific request. This can be useful if you want to disable analytics for a specific user or guild, etc.
66+
67+
```js
68+
import { noAnalytics } from 'commandkit/analytics';
69+
70+
// call inside command or event or middlewares
71+
if (someCondition) {
72+
noAnalytics();
73+
}
74+
```

packages/analytics/package.json

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
{
2+
"name": "@commandkit/analytics",
3+
"version": "0.1.0",
4+
"description": "Analytics providers for CommandKit",
5+
"files": [
6+
"dist"
7+
],
8+
"exports": {
9+
"./umami": {
10+
"import": "./dist/umami/index.js",
11+
"types": "./dist/umami/index.d.ts"
12+
},
13+
"./posthog": {
14+
"import": "./dist/posthog/index.js",
15+
"types": "./dist/posthog/index.d.ts"
16+
}
17+
},
18+
"scripts": {
19+
"lint": "tsc --noEmit",
20+
"build": "tsc"
21+
},
22+
"repository": {
23+
"type": "git",
24+
"url": "git+https://github.com/underctrl-io/commandkit.git"
25+
},
26+
"keywords": [
27+
"commandkit",
28+
"analytics"
29+
],
30+
"author": "twilight <[email protected]>",
31+
"license": "MIT",
32+
"bugs": {
33+
"url": "https://github.com/underctrl-io/commandkit/issues"
34+
},
35+
"homepage": "https://github.com/underctrl-io/commandkit#readme",
36+
"devDependencies": {
37+
"@umami/node": "^0.4.0",
38+
"commandkit": "workspace:*",
39+
"posthog-node": "^4.18.0",
40+
"tsconfig": "workspace:*",
41+
"typescript": "^5.7.3"
42+
}
43+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import { PostHogPlugin, PostHogPluginOptions } from './plugin';
2+
3+
export function posthog(options: PostHogPluginOptions) {
4+
return new PostHogPlugin(options);
5+
}
6+
7+
export * from './plugin';
8+
export * from './provider';
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import { CommandKitPluginRuntime, RuntimePlugin } from 'commandkit/plugin';
2+
import { PostHog, PostHogOptions } from 'posthog-node';
3+
import { PostHogProvider } from './provider';
4+
5+
export interface PostHogPluginOptions {
6+
posthogOptions: {
7+
apiKey: string;
8+
options?: PostHogOptions;
9+
};
10+
}
11+
12+
export class PostHogPlugin extends RuntimePlugin<PostHogPluginOptions> {
13+
public readonly name = 'PostHog';
14+
15+
private provider: PostHogProvider | null = null;
16+
17+
public async activate(ctx: CommandKitPluginRuntime): Promise<void> {
18+
const posthog = new PostHog(
19+
this.options.posthogOptions.apiKey,
20+
this.options.posthogOptions.options,
21+
);
22+
23+
this.provider = new PostHogProvider(posthog);
24+
25+
ctx.commandkit.analytics.registerProvider(this.provider);
26+
}
27+
28+
public async deactivate(ctx: CommandKitPluginRuntime): Promise<void> {
29+
if (!this.provider) return;
30+
31+
await this.provider.posthog.shutdown();
32+
33+
ctx.commandkit.analytics.removeProvider(this.provider);
34+
}
35+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import { PostHog } from 'posthog-node';
2+
import {
3+
AnalyticsEngine,
4+
AnalyticsEvent,
5+
AnalyticsProvider,
6+
IdentifyEvent,
7+
} from 'commandkit/analytics';
8+
9+
export class PostHogProvider implements AnalyticsProvider {
10+
public readonly name = 'PostHog';
11+
public constructor(public readonly posthog: PostHog) {}
12+
13+
public async identify(
14+
engine: AnalyticsEngine,
15+
event: IdentifyEvent,
16+
): Promise<void> {
17+
const client = engine.commandkit.client;
18+
19+
this.posthog.identify({
20+
...event,
21+
distinctId: event.id ?? client.user?.id ?? 'commandkit',
22+
});
23+
}
24+
25+
public async track(
26+
engine: AnalyticsEngine,
27+
event: AnalyticsEvent,
28+
): Promise<void> {
29+
const client = engine.commandkit.client;
30+
31+
this.posthog.capture({
32+
distinctId:
33+
'id' in event && typeof event.id === 'string'
34+
? event.id
35+
: (client.user?.id ?? 'commandkit'),
36+
event: event.name,
37+
properties: event.data,
38+
});
39+
}
40+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import { UmamiPlugin, UmamiPluginOptions } from './plugin';
2+
3+
export function umami(options: UmamiPluginOptions) {
4+
return new UmamiPlugin(options);
5+
}
6+
7+
export * from './plugin';
8+
export * from './provider';
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import { UmamiOptions, Umami } from '@umami/node';
2+
import { CommandKitPluginRuntime, RuntimePlugin } from 'commandkit/plugin';
3+
import { UmamiProvider } from './provider';
4+
5+
export interface UmamiPluginOptions {
6+
umamiOptions: UmamiOptions;
7+
}
8+
9+
export class UmamiPlugin extends RuntimePlugin<UmamiPluginOptions> {
10+
public readonly name = 'Umami';
11+
12+
private provider: UmamiProvider | null = null;
13+
14+
public async activate(ctx: CommandKitPluginRuntime): Promise<void> {
15+
const umami = new Umami(this.options.umamiOptions);
16+
17+
this.provider = new UmamiProvider(umami);
18+
19+
ctx.commandkit.analytics.registerProvider(this.provider);
20+
}
21+
22+
public async deactivate(ctx: CommandKitPluginRuntime): Promise<void> {
23+
if (!this.provider) return;
24+
ctx.commandkit.analytics.removeProvider(this.provider);
25+
}
26+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import { Umami } from '@umami/node';
2+
import {
3+
AnalyticsEngine,
4+
AnalyticsEvent,
5+
AnalyticsProvider,
6+
IdentifyEvent,
7+
} from 'commandkit/analytics';
8+
9+
export class UmamiProvider implements AnalyticsProvider {
10+
public readonly name = 'Umami';
11+
public constructor(public readonly umami: Umami) {}
12+
13+
public async identify(
14+
engine: AnalyticsEngine,
15+
event: IdentifyEvent,
16+
): Promise<void> {
17+
void engine;
18+
await this.umami.identify(event);
19+
}
20+
21+
public async track(
22+
engine: AnalyticsEngine,
23+
event: AnalyticsEvent,
24+
): Promise<void> {
25+
void engine;
26+
await this.umami.track(event.name, event.data);
27+
}
28+
}

packages/analytics/tsconfig.json

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
{
2+
"extends": "tsconfig/base.json",
3+
"compilerOptions": {
4+
"outDir": "dist",
5+
"skipLibCheck": true,
6+
"skipDefaultLibCheck": true,
7+
"declaration": true,
8+
"inlineSourceMap": true,
9+
"target": "ES2020",
10+
"module": "CommonJS",
11+
"noEmit": false
12+
},
13+
"include": ["src"],
14+
"exclude": ["node_modules"]
15+
}

0 commit comments

Comments
 (0)