@theholocron/holocron-plugin-posthog 3.25.1
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/LICENSE +21 -0
- package/README.md +56 -0
- package/dist/index.d.mts +110 -0
- package/dist/index.mjs +113 -0
- package/package.json +64 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Newton Koumantzelis
|
|
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/README.md
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
<!-- editorconfig-checker-disable-file -->
|
|
2
|
+
|
|
3
|
+
# `@theholocron/holocron-plugin-posthog`
|
|
4
|
+
|
|
5
|
+
PostHog plugin for [Holocron](../cli). Implements the `analytics`
|
|
6
|
+
capability against the [PostHog API](https://posthog.com/docs/api) —
|
|
7
|
+
project provisioning and tracking token retrieval.
|
|
8
|
+
|
|
9
|
+
## Install
|
|
10
|
+
|
|
11
|
+
<!-- prettier-ignore -->
|
|
12
|
+
```bash
|
|
13
|
+
pnpm add -D @theholocron/holocron-plugin-posthog
|
|
14
|
+
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Auth
|
|
18
|
+
|
|
19
|
+
Token resolution order:
|
|
20
|
+
|
|
21
|
+
1. `--token <KEY>` flag on the holocron invocation
|
|
22
|
+
2. `HOLOCRON_POSTHOG_TOKEN` env var
|
|
23
|
+
3. `POSTHOG_PERSONAL_API_KEY` env var
|
|
24
|
+
|
|
25
|
+
The token must be a **personal API key** (`phx_*`), found at
|
|
26
|
+
**app.posthog.com → Settings → User → Personal API keys**. This is
|
|
27
|
+
distinct from the project API key (`phc_*`) that the app embeds at
|
|
28
|
+
runtime — the personal key is for management only.
|
|
29
|
+
|
|
30
|
+
## Config
|
|
31
|
+
|
|
32
|
+
<!-- prettier-ignore -->
|
|
33
|
+
```jsonc
|
|
34
|
+
{
|
|
35
|
+
"providers": {
|
|
36
|
+
"analytics": ["posthog", { "host": "https://eu.posthog.com" }],
|
|
37
|
+
},
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
- `host` (optional) — PostHog instance base URL. Defaults to the US
|
|
43
|
+
cloud (`https://app.posthog.com`). Set to `https://eu.posthog.com`
|
|
44
|
+
for EU cloud or your own URL for self-hosted instances.
|
|
45
|
+
|
|
46
|
+
## What's implemented
|
|
47
|
+
|
|
48
|
+
| Method | What it does |
|
|
49
|
+
| --------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
50
|
+
| `describe` | Returns `{ provider: "posthog", envKeys: ["NEXT_PUBLIC_POSTHOG_KEY", "NEXT_PUBLIC_POSTHOG_HOST"] }` — the env vars the app reads at runtime. |
|
|
51
|
+
| `whoami` | Calls `/api/users/@me/` to verify the token and return the org slug. |
|
|
52
|
+
| `ensureProject` | Lists projects and returns the existing one if found by name; creates it via `POST /api/projects/` otherwise. Returns the project's `api_token` (`phc_*`) and `alreadyExists`. |
|
|
53
|
+
|
|
54
|
+
`holocron setup` calls `ensureProject` and pushes `NEXT_PUBLIC_POSTHOG_KEY`
|
|
55
|
+
(the project `api_token`) and `NEXT_PUBLIC_POSTHOG_HOST` (the resolved
|
|
56
|
+
`host` value) to GitHub Secrets.
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { Analytics, AuthError, ResolveTokenInput } from "@theholocron/cli";
|
|
2
|
+
//#region src/auth.d.ts
|
|
3
|
+
declare const resolveToken: (input?: ResolveTokenInput) => string;
|
|
4
|
+
//#endregion
|
|
5
|
+
//#region src/rest.d.ts
|
|
6
|
+
interface PostHogClientOptions {
|
|
7
|
+
token: string;
|
|
8
|
+
/** PostHog instance host. Default: https://app.posthog.com */
|
|
9
|
+
host?: string;
|
|
10
|
+
/** Override base URL for tests (takes precedence over host). */
|
|
11
|
+
baseUrl?: string;
|
|
12
|
+
fetch?: typeof fetch;
|
|
13
|
+
}
|
|
14
|
+
interface PostHogProject {
|
|
15
|
+
id: number;
|
|
16
|
+
name: string;
|
|
17
|
+
api_token: string;
|
|
18
|
+
}
|
|
19
|
+
interface PostHogUser {
|
|
20
|
+
email: string;
|
|
21
|
+
organization: {
|
|
22
|
+
slug: string;
|
|
23
|
+
name: string;
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
interface PostHogClient {
|
|
27
|
+
users: {
|
|
28
|
+
me(): Promise<PostHogUser>;
|
|
29
|
+
};
|
|
30
|
+
projects: {
|
|
31
|
+
list(): Promise<{
|
|
32
|
+
results: PostHogProject[];
|
|
33
|
+
}>;
|
|
34
|
+
create(input: {
|
|
35
|
+
name: string;
|
|
36
|
+
}): Promise<PostHogProject>;
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
declare function createPostHogClient({ token, host, baseUrl, fetch: fetchImpl }: PostHogClientOptions): PostHogClient;
|
|
40
|
+
//#endregion
|
|
41
|
+
//#region src/capabilities/analytics.d.ts
|
|
42
|
+
interface PostHogAnalyticsOptions {
|
|
43
|
+
/** PostHog instance host — pushed as NEXT_PUBLIC_POSTHOG_HOST. Default: https://app.posthog.com */
|
|
44
|
+
host?: string;
|
|
45
|
+
}
|
|
46
|
+
declare class PostHogAnalytics implements Analytics {
|
|
47
|
+
private readonly client;
|
|
48
|
+
private readonly opts;
|
|
49
|
+
readonly key: "analytics";
|
|
50
|
+
readonly providerName = "posthog";
|
|
51
|
+
constructor(client: PostHogClient, opts: PostHogAnalyticsOptions);
|
|
52
|
+
describe(): Promise<{
|
|
53
|
+
provider: string;
|
|
54
|
+
envKeys: string[];
|
|
55
|
+
}>;
|
|
56
|
+
whoami(): Promise<{
|
|
57
|
+
org: string;
|
|
58
|
+
}>;
|
|
59
|
+
ensureProject(name: string): Promise<{
|
|
60
|
+
token: string;
|
|
61
|
+
alreadyExists: boolean;
|
|
62
|
+
}>;
|
|
63
|
+
/** The host value to push as NEXT_PUBLIC_POSTHOG_HOST. */
|
|
64
|
+
get resolvedHost(): string;
|
|
65
|
+
}
|
|
66
|
+
//#endregion
|
|
67
|
+
//#region src/verify-token.d.ts
|
|
68
|
+
interface VerifyTokenSuccess {
|
|
69
|
+
ok: true;
|
|
70
|
+
subject: string;
|
|
71
|
+
}
|
|
72
|
+
interface VerifyTokenFailure {
|
|
73
|
+
ok: false;
|
|
74
|
+
message: string;
|
|
75
|
+
}
|
|
76
|
+
type VerifyTokenResult = VerifyTokenSuccess | VerifyTokenFailure;
|
|
77
|
+
interface VerifyTokenOptions {
|
|
78
|
+
host?: string;
|
|
79
|
+
baseUrl?: string;
|
|
80
|
+
fetch?: typeof fetch;
|
|
81
|
+
}
|
|
82
|
+
declare function verifyToken(token: string, opts?: VerifyTokenOptions): Promise<VerifyTokenResult>;
|
|
83
|
+
//#endregion
|
|
84
|
+
//#region src/index.d.ts
|
|
85
|
+
interface PostHogPluginOptions extends ResolveTokenInput, PostHogAnalyticsOptions {
|
|
86
|
+
/**
|
|
87
|
+
* PostHog instance host. Defaults to US cloud.
|
|
88
|
+
* EU cloud: "https://eu.posthog.com"
|
|
89
|
+
* Self-hosted: your own base URL.
|
|
90
|
+
*/
|
|
91
|
+
host?: string;
|
|
92
|
+
/** Override base URL for tests (takes precedence over host). */
|
|
93
|
+
baseUrl?: string;
|
|
94
|
+
fetch?: typeof fetch;
|
|
95
|
+
}
|
|
96
|
+
interface PluginContext {
|
|
97
|
+
options: PostHogPluginOptions;
|
|
98
|
+
client: PostHogClient;
|
|
99
|
+
}
|
|
100
|
+
declare function createContext(options?: PostHogPluginOptions): PluginContext;
|
|
101
|
+
declare function analytics(ctx: PluginContext): Analytics;
|
|
102
|
+
declare function createPlugin(options?: PostHogPluginOptions): {
|
|
103
|
+
name: string;
|
|
104
|
+
capabilities: {
|
|
105
|
+
analytics: () => Analytics;
|
|
106
|
+
};
|
|
107
|
+
};
|
|
108
|
+
declare const AUTH_HINT: string;
|
|
109
|
+
//#endregion
|
|
110
|
+
export { AUTH_HINT, AuthError, PluginContext, PostHogAnalytics, type PostHogAnalyticsOptions, type PostHogClient, type PostHogClientOptions, PostHogPluginOptions, type PostHogProject, type PostHogUser, type ResolveTokenInput, type VerifyTokenFailure, type VerifyTokenResult, type VerifyTokenSuccess, analytics, createContext, createPlugin, createPostHogClient, resolveToken, verifyToken };
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { AuthError, createResolveToken, createRestClient } from "@theholocron/cli";
|
|
2
|
+
//#region src/auth.ts
|
|
3
|
+
const resolveToken = createResolveToken({
|
|
4
|
+
envName: "HOLOCRON_POSTHOG_TOKEN",
|
|
5
|
+
vendorEnvName: "POSTHOG_PERSONAL_API_KEY",
|
|
6
|
+
keyringService: "posthog",
|
|
7
|
+
errorMessage: "no PostHog personal API key found. Pass --token <KEY>, set HOLOCRON_POSTHOG_TOKEN / POSTHOG_PERSONAL_API_KEY, or run: holocron auth set posthog <phx_KEY>"
|
|
8
|
+
});
|
|
9
|
+
//#endregion
|
|
10
|
+
//#region src/capabilities/analytics.ts
|
|
11
|
+
var PostHogAnalytics = class {
|
|
12
|
+
client;
|
|
13
|
+
opts;
|
|
14
|
+
key = "analytics";
|
|
15
|
+
providerName = "posthog";
|
|
16
|
+
constructor(client, opts) {
|
|
17
|
+
this.client = client;
|
|
18
|
+
this.opts = opts;
|
|
19
|
+
}
|
|
20
|
+
async describe() {
|
|
21
|
+
return {
|
|
22
|
+
provider: "posthog",
|
|
23
|
+
envKeys: ["NEXT_PUBLIC_POSTHOG_KEY", "NEXT_PUBLIC_POSTHOG_HOST"]
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
async whoami() {
|
|
27
|
+
return { org: (await this.client.users.me()).organization.slug };
|
|
28
|
+
}
|
|
29
|
+
async ensureProject(name) {
|
|
30
|
+
const { results } = await this.client.projects.list();
|
|
31
|
+
const found = results.find((p) => p.name === name);
|
|
32
|
+
if (found) return {
|
|
33
|
+
token: found.api_token,
|
|
34
|
+
alreadyExists: true
|
|
35
|
+
};
|
|
36
|
+
return {
|
|
37
|
+
token: (await this.client.projects.create({ name })).api_token,
|
|
38
|
+
alreadyExists: false
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
/** The host value to push as NEXT_PUBLIC_POSTHOG_HOST. */
|
|
42
|
+
get resolvedHost() {
|
|
43
|
+
return this.opts.host ?? "https://app.posthog.com";
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
//#endregion
|
|
47
|
+
//#region src/rest.ts
|
|
48
|
+
function createPostHogClient({ token, host, baseUrl, fetch: fetchImpl }) {
|
|
49
|
+
const client = createRestClient({
|
|
50
|
+
baseUrl: baseUrl ?? host ?? "https://app.posthog.com",
|
|
51
|
+
token,
|
|
52
|
+
vendor: "PostHog",
|
|
53
|
+
fetch: fetchImpl
|
|
54
|
+
});
|
|
55
|
+
return {
|
|
56
|
+
users: { me: () => client.request("/api/users/@me/") },
|
|
57
|
+
projects: {
|
|
58
|
+
list: () => client.request("/api/projects/"),
|
|
59
|
+
create: (input) => client.request("/api/projects/", {
|
|
60
|
+
method: "POST",
|
|
61
|
+
body: input
|
|
62
|
+
})
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
//#endregion
|
|
67
|
+
//#region src/verify-token.ts
|
|
68
|
+
async function verifyToken(token, opts = {}) {
|
|
69
|
+
const client = createPostHogClient({
|
|
70
|
+
token,
|
|
71
|
+
host: opts.host,
|
|
72
|
+
baseUrl: opts.baseUrl,
|
|
73
|
+
fetch: opts.fetch
|
|
74
|
+
});
|
|
75
|
+
try {
|
|
76
|
+
const user = await client.users.me();
|
|
77
|
+
return {
|
|
78
|
+
ok: true,
|
|
79
|
+
subject: `${user.email} @ ${user.organization.slug}`
|
|
80
|
+
};
|
|
81
|
+
} catch (err) {
|
|
82
|
+
return {
|
|
83
|
+
ok: false,
|
|
84
|
+
message: err instanceof Error ? err.message : String(err)
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
//#endregion
|
|
89
|
+
//#region src/index.ts
|
|
90
|
+
function createContext(options = {}) {
|
|
91
|
+
return {
|
|
92
|
+
options,
|
|
93
|
+
client: createPostHogClient({
|
|
94
|
+
token: resolveToken(options),
|
|
95
|
+
host: options.host,
|
|
96
|
+
baseUrl: options.baseUrl,
|
|
97
|
+
fetch: options.fetch
|
|
98
|
+
})
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
function analytics(ctx) {
|
|
102
|
+
return new PostHogAnalytics(ctx.client, { host: ctx.options.host });
|
|
103
|
+
}
|
|
104
|
+
function createPlugin(options = {}) {
|
|
105
|
+
const ctx = createContext(options);
|
|
106
|
+
return {
|
|
107
|
+
name: "@theholocron/holocron-plugin-posthog",
|
|
108
|
+
capabilities: { analytics: () => analytics(ctx) }
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
const AUTH_HINT = "create a personal API key at https://app.posthog.com/settings/user/api-keys (not the project API key — that is the client-side phc_* tracking token), then run: holocron auth set posthog <phx_KEY>";
|
|
112
|
+
//#endregion
|
|
113
|
+
export { AUTH_HINT, AuthError, PostHogAnalytics, analytics, createContext, createPlugin, createPostHogClient, resolveToken, verifyToken };
|
package/package.json
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@theholocron/holocron-plugin-posthog",
|
|
3
|
+
"version": "3.25.1",
|
|
4
|
+
"description": "Holocron plugin for PostHog. Implements the analytics capability — project provisioning and tracking token retrieval.",
|
|
5
|
+
"homepage": "https://github.com/theholocron/holocron/tree/main/packages/holocron-plugin-posthog#readme",
|
|
6
|
+
"bugs": "https://github.com/theholocron/holocron/issues",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/theholocron/holocron.git",
|
|
10
|
+
"directory": "packages/holocron-plugin-posthog"
|
|
11
|
+
},
|
|
12
|
+
"license": "MIT",
|
|
13
|
+
"author": "Newton Koumantzelis",
|
|
14
|
+
"type": "module",
|
|
15
|
+
"main": "./dist/index.mjs",
|
|
16
|
+
"exports": {
|
|
17
|
+
".": {
|
|
18
|
+
"types": "./dist/index.d.mts",
|
|
19
|
+
"import": "./dist/index.mjs",
|
|
20
|
+
"default": "./dist/index.mjs"
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
"peerDependencies": {
|
|
24
|
+
"@theholocron/cli": "3.26.0"
|
|
25
|
+
},
|
|
26
|
+
"devDependencies": {
|
|
27
|
+
"@theholocron/eslint-config": "^7.19.1",
|
|
28
|
+
"@theholocron/tsconfig": "^7.19.1",
|
|
29
|
+
"@theholocron/tsdown-config": "^7.19.1",
|
|
30
|
+
"@theholocron/vitest-config": "^7.19.1",
|
|
31
|
+
"@types/node": "^26",
|
|
32
|
+
"@vitest/coverage-v8": "^4.1.10",
|
|
33
|
+
"@vitest/eslint-plugin": "^1.6.27",
|
|
34
|
+
"chalk": "^6.0.0",
|
|
35
|
+
"eslint": "^10.8.1",
|
|
36
|
+
"eslint-plugin-n": "^18.3.0",
|
|
37
|
+
"globals": "^17.9.0",
|
|
38
|
+
"tsdown": "^0.22.14",
|
|
39
|
+
"tsx": "4.22.4",
|
|
40
|
+
"typescript": "^5.9.3",
|
|
41
|
+
"vitest": "^4.1.10",
|
|
42
|
+
"@theholocron/cli": "3.26.0"
|
|
43
|
+
},
|
|
44
|
+
"publishConfig": {
|
|
45
|
+
"access": "public"
|
|
46
|
+
},
|
|
47
|
+
"files": [
|
|
48
|
+
"dist",
|
|
49
|
+
"README.md"
|
|
50
|
+
],
|
|
51
|
+
"types": "./dist/index.d.mts",
|
|
52
|
+
"engines": {
|
|
53
|
+
"node": ">=22"
|
|
54
|
+
},
|
|
55
|
+
"scripts": {
|
|
56
|
+
"build": "tsdown",
|
|
57
|
+
"lint": "eslint .",
|
|
58
|
+
"typecheck": "tsc --noEmit",
|
|
59
|
+
"test": "vitest run",
|
|
60
|
+
"test:watch": "vitest",
|
|
61
|
+
"test:coverage": "vitest run --coverage",
|
|
62
|
+
"validate": "tsx scripts/validate.mjs"
|
|
63
|
+
}
|
|
64
|
+
}
|