@apideck/agent-analytics 0.11.0 → 0.12.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/README.md +56 -0
- package/dist/adapters/posthog.cjs +2 -30
- package/dist/adapters/posthog.cjs.map +1 -1
- package/dist/adapters/posthog.d.cts +7 -1
- package/dist/adapters/posthog.d.ts +7 -1
- package/dist/adapters/posthog.js +2 -28
- package/dist/adapters/posthog.js.map +1 -1
- package/dist/adapters/webhook.cjs +1 -23
- package/dist/adapters/webhook.cjs.map +1 -1
- package/dist/adapters/webhook.d.cts +3 -1
- package/dist/adapters/webhook.d.ts +3 -1
- package/dist/adapters/webhook.js +1 -21
- package/dist/adapters/webhook.js.map +1 -1
- package/dist/index.cjs +2 -900
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +37 -100
- package/dist/index.d.ts +37 -100
- package/dist/index.js +2 -875
- package/dist/index.js.map +1 -1
- package/dist/markdown.cjs +2 -67
- package/dist/markdown.cjs.map +1 -1
- package/dist/markdown.js +2 -63
- package/dist/markdown.js.map +1 -1
- package/dist/{types-DKqlfVz6.d.cts → types-sQoQK-ox.d.cts} +31 -2
- package/dist/{types-DKqlfVz6.d.ts → types-sQoQK-ox.d.ts} +31 -2
- package/dist/verify.cjs +3 -0
- package/dist/verify.cjs.map +1 -0
- package/dist/verify.d.cts +50 -0
- package/dist/verify.d.ts +50 -0
- package/dist/verify.js +3 -0
- package/dist/verify.js.map +1 -0
- package/package.json +7 -2
package/README.md
CHANGED
|
@@ -82,6 +82,62 @@ Now you can build:
|
|
|
82
82
|
|
|
83
83
|
---
|
|
84
84
|
|
|
85
|
+
## Upgrading to 0.12
|
|
86
|
+
|
|
87
|
+
Four breaking changes, all deliberate. Each one existed because the previous
|
|
88
|
+
behaviour was wrong in a way that failed quietly.
|
|
89
|
+
|
|
90
|
+
**`distinctId` is now keyed.** The old identifier was an unsalted 32-bit djb2
|
|
91
|
+
over `ip:userAgent`. Since the user agent ships in plaintext on the same event,
|
|
92
|
+
only the IP had to be searched — a laptop recovered a residential address in
|
|
93
|
+
75 seconds. Set `idSecret` (or `AGENT_ANALYTICS_ID_SECRET`) to a stable secret;
|
|
94
|
+
without one, a random per-instance secret is used, which stays private but
|
|
95
|
+
means ids no longer correlate across instances. Existing ids will not match the
|
|
96
|
+
new ones either way.
|
|
97
|
+
|
|
98
|
+
```diff
|
|
99
|
+
- void trackVisit(req, { analytics })
|
|
100
|
+
+ void trackVisit(req, { analytics, idSecret: process.env.AGENT_ANALYTICS_ID_SECRET })
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
**`verifyIdentity: true` is replaced by an injected verifier.** The published
|
|
104
|
+
IP range tables are the largest thing in the package, and importing them from
|
|
105
|
+
the root entry shipped them to every consumer whether or not they verified
|
|
106
|
+
anything. They now live behind `@apideck/agent-analytics/verify`.
|
|
107
|
+
|
|
108
|
+
```diff
|
|
109
|
+
- void trackVisit(req, { analytics, verifyIdentity: true })
|
|
110
|
+
+ import { verifyRequest } from '@apideck/agent-analytics/verify'
|
|
111
|
+
+ void trackVisit(req, { analytics, verify: verifyRequest })
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
**Caller `properties` no longer override computed fields.** They were spread
|
|
115
|
+
last, so `properties: { path }` silently replaced the real path and
|
|
116
|
+
`properties: { is_ai_bot }` could contradict the classification on the same
|
|
117
|
+
event. Non-colliding keys are unaffected.
|
|
118
|
+
|
|
119
|
+
**Headless automation is labelled `Headless`, not `Browser`.** A browser user
|
|
120
|
+
agent with headless headers accounted for 79% of one production site's agent
|
|
121
|
+
traffic, and calling it `Browser` hid it behind the obvious
|
|
122
|
+
`bot_name != 'Browser'` filter. `headless_score` and `headless_likely` are now
|
|
123
|
+
omitted on declared crawlers and HTTP clients, where they fired on 99% of
|
|
124
|
+
events and carried no signal.
|
|
125
|
+
|
|
126
|
+
**Node 18 is no longer supported; the minimum is Node 20.** `globalThis.crypto`
|
|
127
|
+
only became available by default in Node 19, and shipping a `node:crypto`
|
|
128
|
+
fallback would mean a static import of a Node builtin in a library whose main
|
|
129
|
+
target is edge runtimes. Node 18 reached end of life in April 2025. Runtimes
|
|
130
|
+
without Web Crypto now fail with an explicit message rather than a confusing
|
|
131
|
+
`undefined` dereference.
|
|
132
|
+
|
|
133
|
+
### Also in 0.12
|
|
134
|
+
|
|
135
|
+
- Adapters surface non-2xx responses as `CaptureTransportError` instead of
|
|
136
|
+
swallowing them. Pass `onError` to `trackVisit` to see them; capture still
|
|
137
|
+
never throws into the response path.
|
|
138
|
+
- Outbound captures carry a 3s `AbortSignal` (`timeoutMs` to change it).
|
|
139
|
+
- Root bundle is 65% smaller (27.7 kB → 9.6 kB, 3.8 kB gzipped).
|
|
140
|
+
|
|
85
141
|
## Install
|
|
86
142
|
|
|
87
143
|
```bash
|
|
@@ -1,31 +1,3 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
// src/adapters/posthog.ts
|
|
4
|
-
function posthogAnalytics(config) {
|
|
5
|
-
const hostRaw = config.host ?? "https://us.i.posthog.com";
|
|
6
|
-
const base = (/^https?:\/\//.test(hostRaw) ? hostRaw : `https://${hostRaw}`).replace(/\/$/, "");
|
|
7
|
-
const path = (config.path ?? "/i/v0/e/").replace(/^(?!\/)/, "/");
|
|
8
|
-
const endpoint = `${base}${path}`;
|
|
9
|
-
const fetchImpl = config.fetchImpl ?? fetch;
|
|
10
|
-
return {
|
|
11
|
-
async capture(event) {
|
|
12
|
-
const payload = {
|
|
13
|
-
api_key: config.apiKey,
|
|
14
|
-
event: event.event,
|
|
15
|
-
distinct_id: event.distinctId,
|
|
16
|
-
timestamp: event.timestamp,
|
|
17
|
-
properties: event.properties
|
|
18
|
-
};
|
|
19
|
-
await fetchImpl(endpoint, {
|
|
20
|
-
method: "POST",
|
|
21
|
-
headers: { "Content-Type": "application/json" },
|
|
22
|
-
body: JSON.stringify(payload),
|
|
23
|
-
keepalive: true
|
|
24
|
-
});
|
|
25
|
-
}
|
|
26
|
-
};
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
exports.posthogAnalytics = posthogAnalytics;
|
|
30
|
-
//# sourceMappingURL=posthog.cjs.map
|
|
1
|
+
'use strict';var r=class extends Error{status;body;constructor(e,a,i){super(e),this.name="CaptureTransportError",this.status=a,this.body=i;}};function h(t){let e=t.host??"https://us.i.posthog.com",a=(/^https?:\/\//.test(e)?e:`https://${e}`).replace(/\/$/,""),i=(t.path??"/i/v0/e/").replace(/^(?!\/)/,"/"),n=`${a}${i}`,p=t.fetchImpl??fetch;return {async capture(o){let c={api_key:t.apiKey,event:o.event,distinct_id:o.distinctId,timestamp:o.timestamp,properties:o.properties},s=await p(n,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(c),keepalive:true,signal:AbortSignal.timeout(t.timeoutMs??3e3)});if(!s.ok)throw new r(`PostHog capture failed: ${s.status} ${s.statusText}`,s.status,await s.text().catch(()=>{}))}}}
|
|
2
|
+
exports.posthogAnalytics=h;//# sourceMappingURL=posthog.cjs.map
|
|
31
3
|
//# sourceMappingURL=posthog.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/adapters/posthog.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"sources":["../../src/errors.ts","../../src/adapters/posthog.ts"],"names":["CaptureTransportError","message","status","body","posthogAnalytics","config","hostRaw","base","path","endpoint","fetchImpl","event","payload","res"],"mappings":"aACO,IAAMA,CAAAA,CAAN,cAAoC,KAAM,CACtC,OACA,IAAA,CACT,WAAA,CAAYC,CAAAA,CAAiBC,CAAAA,CAAiBC,CAAAA,CAAe,CAC3D,KAAA,CAAMF,CAAO,EACb,IAAA,CAAK,IAAA,CAAO,uBAAA,CACZ,IAAA,CAAK,MAAA,CAASC,CAAAA,CACd,IAAA,CAAK,IAAA,CAAOC,EACd,CACF,CAAA,CC2BO,SAASC,CAAAA,CAAiBC,EAAgD,CAC/E,IAAMC,CAAAA,CAAUD,CAAAA,CAAO,MAAQ,0BAAA,CACzBE,CAAAA,CAAAA,CAAQ,cAAA,CAAe,IAAA,CAAKD,CAAO,CAAA,CAAIA,CAAAA,CAAU,CAAA,QAAA,EAAWA,CAAO,CAAA,CAAA,EAAI,OAAA,CAAQ,KAAA,CAAO,EAAE,CAAA,CACxFE,CAAAA,CAAAA,CAAQH,CAAAA,CAAO,IAAA,EAAQ,YAAY,OAAA,CAAQ,SAAA,CAAW,GAAG,CAAA,CACzDI,CAAAA,CAAW,CAAA,EAAGF,CAAI,CAAA,EAAGC,CAAI,CAAA,CAAA,CACzBE,CAAAA,CAAYL,CAAAA,CAAO,SAAA,EAAa,MAEtC,OAAO,CACL,MAAM,OAAA,CAAQM,EAAoC,CAChD,IAAMC,CAAAA,CAAU,CACd,OAAA,CAASP,CAAAA,CAAO,MAAA,CAChB,KAAA,CAAOM,EAAM,KAAA,CACb,WAAA,CAAaA,CAAAA,CAAM,UAAA,CACnB,SAAA,CAAWA,CAAAA,CAAM,SAAA,CACjB,UAAA,CAAYA,EAAM,UACpB,CAAA,CAIME,CAAAA,CAAM,MAAMH,CAAAA,CAAUD,CAAAA,CAAU,CACpC,MAAA,CAAQ,OACR,OAAA,CAAS,CAAE,cAAA,CAAgB,kBAAmB,EAC9C,IAAA,CAAM,IAAA,CAAK,SAAA,CAAUG,CAAO,EAC5B,SAAA,CAAW,IAAA,CACX,MAAA,CAAQ,WAAA,CAAY,OAAA,CAAQP,CAAAA,CAAO,SAAA,EAAa,GAAI,CACtD,CAAC,CAAA,CACD,GAAI,CAACQ,CAAAA,CAAI,EAAA,CACP,MAAM,IAAIb,EACR,CAAA,wBAAA,EAA2Ba,CAAAA,CAAI,MAAM,CAAA,CAAA,EAAIA,CAAAA,CAAI,UAAU,CAAA,CAAA,CACvDA,CAAAA,CAAI,OACJ,MAAMA,CAAAA,CAAI,IAAA,EAAK,CAAE,MAAM,IAAG,CAAA,CAAY,CACxC,CAEJ,CACF,CACF","file":"posthog.cjs","sourcesContent":["/** Thrown when the analytics backend rejects, errors, or times out a capture. */\nexport class CaptureTransportError extends Error {\n readonly status: number | undefined\n readonly body: string | undefined\n constructor(message: string, status?: number, body?: string) {\n super(message)\n this.name = 'CaptureTransportError'\n this.status = status\n this.body = body\n }\n}\n","import type { AnalyticsAdapter, CaptureEvent } from '../types.js'\nimport { CaptureTransportError } from '../errors.js'\n\n\nexport interface PostHogAdapterConfig {\n /** PostHog project API key (the public one used by the JS SDK). */\n apiKey: string\n /**\n * PostHog host, with or without scheme. Defaults to `https://us.i.posthog.com`.\n * Use `https://eu.i.posthog.com` for EU cloud, or your own reverse-proxy\n * domain (e.g. `https://svc.example.com`).\n */\n host?: string\n /**\n * Path on the host that accepts single-event captures. Defaults to\n * `/i/v0/e/` which is PostHog's current endpoint for this.\n */\n path?: string\n /**\n * Override the `fetch` implementation (useful for tests or custom runtimes\n * that need a pinned fetch).\n */\n fetchImpl?: typeof fetch\n /**\n * Abort the capture after this many milliseconds. Defaults to 3000. Without\n * a bound, a hung backend leaves a pending promise for the lifetime of an\n * edge invocation.\n */\n timeoutMs?: number\n}\n\n/**\n * Adapter that posts each event to the PostHog capture endpoint. Uses\n * `keepalive: true` so the request survives after a serverless response\n * returns — events aren't guaranteed (fire-and-forget), but that's the\n * trade we want to keep the hot path fast.\n */\nexport function posthogAnalytics(config: PostHogAdapterConfig): AnalyticsAdapter {\n const hostRaw = config.host ?? 'https://us.i.posthog.com'\n const base = (/^https?:\\/\\//.test(hostRaw) ? hostRaw : `https://${hostRaw}`).replace(/\\/$/, '')\n const path = (config.path ?? '/i/v0/e/').replace(/^(?!\\/)/, '/')\n const endpoint = `${base}${path}`\n const fetchImpl = config.fetchImpl ?? fetch\n\n return {\n async capture(event: CaptureEvent): Promise<void> {\n const payload = {\n api_key: config.apiKey,\n event: event.event,\n distinct_id: event.distinctId,\n timestamp: event.timestamp,\n properties: event.properties\n }\n // A 401 from a mistyped key used to look identical to success. Surface\n // it: `trackVisit` routes it to `onError` and still never throws into\n // the response path.\n const res = await fetchImpl(endpoint, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(payload),\n keepalive: true,\n signal: AbortSignal.timeout(config.timeoutMs ?? 3000)\n })\n if (!res.ok) {\n throw new CaptureTransportError(\n `PostHog capture failed: ${res.status} ${res.statusText}`,\n res.status,\n await res.text().catch(() => undefined)\n )\n }\n }\n }\n}\n"]}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { A as AnalyticsAdapter } from '../types-
|
|
1
|
+
import { A as AnalyticsAdapter } from '../types-sQoQK-ox.cjs';
|
|
2
2
|
|
|
3
3
|
interface PostHogAdapterConfig {
|
|
4
4
|
/** PostHog project API key (the public one used by the JS SDK). */
|
|
@@ -19,6 +19,12 @@ interface PostHogAdapterConfig {
|
|
|
19
19
|
* that need a pinned fetch).
|
|
20
20
|
*/
|
|
21
21
|
fetchImpl?: typeof fetch;
|
|
22
|
+
/**
|
|
23
|
+
* Abort the capture after this many milliseconds. Defaults to 3000. Without
|
|
24
|
+
* a bound, a hung backend leaves a pending promise for the lifetime of an
|
|
25
|
+
* edge invocation.
|
|
26
|
+
*/
|
|
27
|
+
timeoutMs?: number;
|
|
22
28
|
}
|
|
23
29
|
/**
|
|
24
30
|
* Adapter that posts each event to the PostHog capture endpoint. Uses
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { A as AnalyticsAdapter } from '../types-
|
|
1
|
+
import { A as AnalyticsAdapter } from '../types-sQoQK-ox.js';
|
|
2
2
|
|
|
3
3
|
interface PostHogAdapterConfig {
|
|
4
4
|
/** PostHog project API key (the public one used by the JS SDK). */
|
|
@@ -19,6 +19,12 @@ interface PostHogAdapterConfig {
|
|
|
19
19
|
* that need a pinned fetch).
|
|
20
20
|
*/
|
|
21
21
|
fetchImpl?: typeof fetch;
|
|
22
|
+
/**
|
|
23
|
+
* Abort the capture after this many milliseconds. Defaults to 3000. Without
|
|
24
|
+
* a bound, a hung backend leaves a pending promise for the lifetime of an
|
|
25
|
+
* edge invocation.
|
|
26
|
+
*/
|
|
27
|
+
timeoutMs?: number;
|
|
22
28
|
}
|
|
23
29
|
/**
|
|
24
30
|
* Adapter that posts each event to the PostHog capture endpoint. Uses
|
package/dist/adapters/posthog.js
CHANGED
|
@@ -1,29 +1,3 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
const hostRaw = config.host ?? "https://us.i.posthog.com";
|
|
4
|
-
const base = (/^https?:\/\//.test(hostRaw) ? hostRaw : `https://${hostRaw}`).replace(/\/$/, "");
|
|
5
|
-
const path = (config.path ?? "/i/v0/e/").replace(/^(?!\/)/, "/");
|
|
6
|
-
const endpoint = `${base}${path}`;
|
|
7
|
-
const fetchImpl = config.fetchImpl ?? fetch;
|
|
8
|
-
return {
|
|
9
|
-
async capture(event) {
|
|
10
|
-
const payload = {
|
|
11
|
-
api_key: config.apiKey,
|
|
12
|
-
event: event.event,
|
|
13
|
-
distinct_id: event.distinctId,
|
|
14
|
-
timestamp: event.timestamp,
|
|
15
|
-
properties: event.properties
|
|
16
|
-
};
|
|
17
|
-
await fetchImpl(endpoint, {
|
|
18
|
-
method: "POST",
|
|
19
|
-
headers: { "Content-Type": "application/json" },
|
|
20
|
-
body: JSON.stringify(payload),
|
|
21
|
-
keepalive: true
|
|
22
|
-
});
|
|
23
|
-
}
|
|
24
|
-
};
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
export { posthogAnalytics };
|
|
28
|
-
//# sourceMappingURL=posthog.js.map
|
|
1
|
+
var r=class extends Error{status;body;constructor(e,a,i){super(e),this.name="CaptureTransportError",this.status=a,this.body=i;}};function h(t){let e=t.host??"https://us.i.posthog.com",a=(/^https?:\/\//.test(e)?e:`https://${e}`).replace(/\/$/,""),i=(t.path??"/i/v0/e/").replace(/^(?!\/)/,"/"),n=`${a}${i}`,p=t.fetchImpl??fetch;return {async capture(o){let c={api_key:t.apiKey,event:o.event,distinct_id:o.distinctId,timestamp:o.timestamp,properties:o.properties},s=await p(n,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(c),keepalive:true,signal:AbortSignal.timeout(t.timeoutMs??3e3)});if(!s.ok)throw new r(`PostHog capture failed: ${s.status} ${s.statusText}`,s.status,await s.text().catch(()=>{}))}}}
|
|
2
|
+
export{h as posthogAnalytics};//# sourceMappingURL=posthog.js.map
|
|
29
3
|
//# sourceMappingURL=posthog.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/adapters/posthog.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"sources":["../../src/errors.ts","../../src/adapters/posthog.ts"],"names":["CaptureTransportError","message","status","body","posthogAnalytics","config","hostRaw","base","path","endpoint","fetchImpl","event","payload","res"],"mappings":"AACO,IAAMA,CAAAA,CAAN,cAAoC,KAAM,CACtC,OACA,IAAA,CACT,WAAA,CAAYC,CAAAA,CAAiBC,CAAAA,CAAiBC,CAAAA,CAAe,CAC3D,KAAA,CAAMF,CAAO,EACb,IAAA,CAAK,IAAA,CAAO,uBAAA,CACZ,IAAA,CAAK,MAAA,CAASC,CAAAA,CACd,IAAA,CAAK,IAAA,CAAOC,EACd,CACF,CAAA,CC2BO,SAASC,CAAAA,CAAiBC,EAAgD,CAC/E,IAAMC,CAAAA,CAAUD,CAAAA,CAAO,MAAQ,0BAAA,CACzBE,CAAAA,CAAAA,CAAQ,cAAA,CAAe,IAAA,CAAKD,CAAO,CAAA,CAAIA,CAAAA,CAAU,CAAA,QAAA,EAAWA,CAAO,CAAA,CAAA,EAAI,OAAA,CAAQ,KAAA,CAAO,EAAE,CAAA,CACxFE,CAAAA,CAAAA,CAAQH,CAAAA,CAAO,IAAA,EAAQ,YAAY,OAAA,CAAQ,SAAA,CAAW,GAAG,CAAA,CACzDI,CAAAA,CAAW,CAAA,EAAGF,CAAI,CAAA,EAAGC,CAAI,CAAA,CAAA,CACzBE,CAAAA,CAAYL,CAAAA,CAAO,SAAA,EAAa,MAEtC,OAAO,CACL,MAAM,OAAA,CAAQM,EAAoC,CAChD,IAAMC,CAAAA,CAAU,CACd,OAAA,CAASP,CAAAA,CAAO,MAAA,CAChB,KAAA,CAAOM,EAAM,KAAA,CACb,WAAA,CAAaA,CAAAA,CAAM,UAAA,CACnB,SAAA,CAAWA,CAAAA,CAAM,SAAA,CACjB,UAAA,CAAYA,EAAM,UACpB,CAAA,CAIME,CAAAA,CAAM,MAAMH,CAAAA,CAAUD,CAAAA,CAAU,CACpC,MAAA,CAAQ,OACR,OAAA,CAAS,CAAE,cAAA,CAAgB,kBAAmB,EAC9C,IAAA,CAAM,IAAA,CAAK,SAAA,CAAUG,CAAO,EAC5B,SAAA,CAAW,IAAA,CACX,MAAA,CAAQ,WAAA,CAAY,OAAA,CAAQP,CAAAA,CAAO,SAAA,EAAa,GAAI,CACtD,CAAC,CAAA,CACD,GAAI,CAACQ,CAAAA,CAAI,EAAA,CACP,MAAM,IAAIb,EACR,CAAA,wBAAA,EAA2Ba,CAAAA,CAAI,MAAM,CAAA,CAAA,EAAIA,CAAAA,CAAI,UAAU,CAAA,CAAA,CACvDA,CAAAA,CAAI,OACJ,MAAMA,CAAAA,CAAI,IAAA,EAAK,CAAE,MAAM,IAAG,CAAA,CAAY,CACxC,CAEJ,CACF,CACF","file":"posthog.js","sourcesContent":["/** Thrown when the analytics backend rejects, errors, or times out a capture. */\nexport class CaptureTransportError extends Error {\n readonly status: number | undefined\n readonly body: string | undefined\n constructor(message: string, status?: number, body?: string) {\n super(message)\n this.name = 'CaptureTransportError'\n this.status = status\n this.body = body\n }\n}\n","import type { AnalyticsAdapter, CaptureEvent } from '../types.js'\nimport { CaptureTransportError } from '../errors.js'\n\n\nexport interface PostHogAdapterConfig {\n /** PostHog project API key (the public one used by the JS SDK). */\n apiKey: string\n /**\n * PostHog host, with or without scheme. Defaults to `https://us.i.posthog.com`.\n * Use `https://eu.i.posthog.com` for EU cloud, or your own reverse-proxy\n * domain (e.g. `https://svc.example.com`).\n */\n host?: string\n /**\n * Path on the host that accepts single-event captures. Defaults to\n * `/i/v0/e/` which is PostHog's current endpoint for this.\n */\n path?: string\n /**\n * Override the `fetch` implementation (useful for tests or custom runtimes\n * that need a pinned fetch).\n */\n fetchImpl?: typeof fetch\n /**\n * Abort the capture after this many milliseconds. Defaults to 3000. Without\n * a bound, a hung backend leaves a pending promise for the lifetime of an\n * edge invocation.\n */\n timeoutMs?: number\n}\n\n/**\n * Adapter that posts each event to the PostHog capture endpoint. Uses\n * `keepalive: true` so the request survives after a serverless response\n * returns — events aren't guaranteed (fire-and-forget), but that's the\n * trade we want to keep the hot path fast.\n */\nexport function posthogAnalytics(config: PostHogAdapterConfig): AnalyticsAdapter {\n const hostRaw = config.host ?? 'https://us.i.posthog.com'\n const base = (/^https?:\\/\\//.test(hostRaw) ? hostRaw : `https://${hostRaw}`).replace(/\\/$/, '')\n const path = (config.path ?? '/i/v0/e/').replace(/^(?!\\/)/, '/')\n const endpoint = `${base}${path}`\n const fetchImpl = config.fetchImpl ?? fetch\n\n return {\n async capture(event: CaptureEvent): Promise<void> {\n const payload = {\n api_key: config.apiKey,\n event: event.event,\n distinct_id: event.distinctId,\n timestamp: event.timestamp,\n properties: event.properties\n }\n // A 401 from a mistyped key used to look identical to success. Surface\n // it: `trackVisit` routes it to `onError` and still never throws into\n // the response path.\n const res = await fetchImpl(endpoint, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(payload),\n keepalive: true,\n signal: AbortSignal.timeout(config.timeoutMs ?? 3000)\n })\n if (!res.ok) {\n throw new CaptureTransportError(\n `PostHog capture failed: ${res.status} ${res.statusText}`,\n res.status,\n await res.text().catch(() => undefined)\n )\n }\n }\n }\n}\n"]}
|
|
@@ -1,24 +1,2 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
// src/adapters/webhook.ts
|
|
4
|
-
function webhookAnalytics(config) {
|
|
5
|
-
const fetchImpl = config.fetchImpl ?? fetch;
|
|
6
|
-
const transform = config.transform ?? ((e) => e);
|
|
7
|
-
return {
|
|
8
|
-
async capture(event) {
|
|
9
|
-
await fetchImpl(config.url, {
|
|
10
|
-
method: "POST",
|
|
11
|
-
headers: {
|
|
12
|
-
"Content-Type": "application/json",
|
|
13
|
-
...config.headers ?? {}
|
|
14
|
-
},
|
|
15
|
-
body: JSON.stringify(transform(event)),
|
|
16
|
-
keepalive: true
|
|
17
|
-
});
|
|
18
|
-
}
|
|
19
|
-
};
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
exports.webhookAnalytics = webhookAnalytics;
|
|
23
|
-
//# sourceMappingURL=webhook.cjs.map
|
|
1
|
+
'use strict';var n=class extends Error{status;body;constructor(s,o,e){super(s),this.name="CaptureTransportError",this.status=o,this.body=e;}};function u(t){let s=t.fetchImpl??fetch,o=t.transform??(e=>e);return {async capture(e){let r=await s(t.url,{method:"POST",headers:{"Content-Type":"application/json",...t.headers??{}},body:JSON.stringify(o(e)),keepalive:true,signal:AbortSignal.timeout(t.timeoutMs??3e3)});if(!r.ok)throw new n(`Webhook capture failed: ${r.status} ${r.statusText}`,r.status,await r.text().catch(()=>{}))}}}exports.webhookAnalytics=u;//# sourceMappingURL=webhook.cjs.map
|
|
24
2
|
//# sourceMappingURL=webhook.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/adapters/webhook.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"sources":["../../src/errors.ts","../../src/adapters/webhook.ts"],"names":["CaptureTransportError","message","status","body","webhookAnalytics","config","fetchImpl","transform","event","res"],"mappings":"aACO,IAAMA,CAAAA,CAAN,cAAoC,KAAM,CACtC,MAAA,CACA,KACT,WAAA,CAAYC,CAAAA,CAAiBC,CAAAA,CAAiBC,CAAAA,CAAe,CAC3D,KAAA,CAAMF,CAAO,CAAA,CACb,IAAA,CAAK,IAAA,CAAO,uBAAA,CACZ,IAAA,CAAK,MAAA,CAASC,EACd,IAAA,CAAK,IAAA,CAAOC,EACd,CACF,CAAA,CCeO,SAASC,EAAiBC,CAAAA,CAAgD,CAC/E,IAAMC,CAAAA,CAAYD,CAAAA,CAAO,SAAA,EAAa,MAChCE,CAAAA,CAAYF,CAAAA,CAAO,SAAA,GAAe,CAAA,EAA6B,CAAA,CAAA,CAErE,OAAO,CACL,MAAM,OAAA,CAAQG,CAAAA,CAAoC,CAChD,IAAMC,CAAAA,CAAM,MAAMH,CAAAA,CAAUD,CAAAA,CAAO,GAAA,CAAK,CACtC,MAAA,CAAQ,MAAA,CACR,QAAS,CACP,cAAA,CAAgB,kBAAA,CAChB,GAAIA,CAAAA,CAAO,OAAA,EAAW,EACxB,CAAA,CACA,IAAA,CAAM,IAAA,CAAK,SAAA,CAAUE,CAAAA,CAAUC,CAAK,CAAC,CAAA,CACrC,SAAA,CAAW,IAAA,CACX,MAAA,CAAQ,WAAA,CAAY,QAAQH,CAAAA,CAAO,SAAA,EAAa,GAAI,CACtD,CAAC,CAAA,CACD,GAAI,CAACI,CAAAA,CAAI,EAAA,CACP,MAAM,IAAIT,CAAAA,CACR,2BAA2BS,CAAAA,CAAI,MAAM,CAAA,CAAA,EAAIA,CAAAA,CAAI,UAAU,CAAA,CAAA,CACvDA,EAAI,MAAA,CACJ,MAAMA,CAAAA,CAAI,IAAA,EAAK,CAAE,KAAA,CAAM,IAAG,CAAA,CAAY,CACxC,CAEJ,CACF,CACF","file":"webhook.cjs","sourcesContent":["/** Thrown when the analytics backend rejects, errors, or times out a capture. */\nexport class CaptureTransportError extends Error {\n readonly status: number | undefined\n readonly body: string | undefined\n constructor(message: string, status?: number, body?: string) {\n super(message)\n this.name = 'CaptureTransportError'\n this.status = status\n this.body = body\n }\n}\n","import type { AnalyticsAdapter, CaptureEvent } from '../types.js'\nimport { CaptureTransportError } from '../errors.js'\n\nexport interface WebhookAdapterConfig {\n /** Destination URL that receives a POST for each event. */\n url: string\n /** Extra headers merged onto the POST (useful for shared-secret auth). */\n headers?: Record<string, string>\n /**\n * Transform the event into the exact JSON body the destination expects.\n * Defaults to sending the {@link CaptureEvent} as-is.\n */\n transform?: (event: CaptureEvent) => unknown\n /** Override the `fetch` implementation. */\n fetchImpl?: typeof fetch\n /** Abort the capture after this many milliseconds. Defaults to 3000. */\n timeoutMs?: number\n}\n\n/**\n * Adapter that POSTs each event to an arbitrary webhook URL. Keeps the\n * library analytics-backend-agnostic — use this when PostHog isn't your\n * analytics of record, or when you want to multiplex events through your\n * own ingestion layer.\n */\nexport function webhookAnalytics(config: WebhookAdapterConfig): AnalyticsAdapter {\n const fetchImpl = config.fetchImpl ?? fetch\n const transform = config.transform ?? ((e: CaptureEvent): unknown => e)\n\n return {\n async capture(event: CaptureEvent): Promise<void> {\n const res = await fetchImpl(config.url, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n ...(config.headers ?? {})\n },\n body: JSON.stringify(transform(event)),\n keepalive: true,\n signal: AbortSignal.timeout(config.timeoutMs ?? 3000)\n })\n if (!res.ok) {\n throw new CaptureTransportError(\n `Webhook capture failed: ${res.status} ${res.statusText}`,\n res.status,\n await res.text().catch(() => undefined)\n )\n }\n }\n }\n}\n"]}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { C as CaptureEvent, A as AnalyticsAdapter } from '../types-
|
|
1
|
+
import { C as CaptureEvent, A as AnalyticsAdapter } from '../types-sQoQK-ox.cjs';
|
|
2
2
|
|
|
3
3
|
interface WebhookAdapterConfig {
|
|
4
4
|
/** Destination URL that receives a POST for each event. */
|
|
@@ -12,6 +12,8 @@ interface WebhookAdapterConfig {
|
|
|
12
12
|
transform?: (event: CaptureEvent) => unknown;
|
|
13
13
|
/** Override the `fetch` implementation. */
|
|
14
14
|
fetchImpl?: typeof fetch;
|
|
15
|
+
/** Abort the capture after this many milliseconds. Defaults to 3000. */
|
|
16
|
+
timeoutMs?: number;
|
|
15
17
|
}
|
|
16
18
|
/**
|
|
17
19
|
* Adapter that POSTs each event to an arbitrary webhook URL. Keeps the
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { C as CaptureEvent, A as AnalyticsAdapter } from '../types-
|
|
1
|
+
import { C as CaptureEvent, A as AnalyticsAdapter } from '../types-sQoQK-ox.js';
|
|
2
2
|
|
|
3
3
|
interface WebhookAdapterConfig {
|
|
4
4
|
/** Destination URL that receives a POST for each event. */
|
|
@@ -12,6 +12,8 @@ interface WebhookAdapterConfig {
|
|
|
12
12
|
transform?: (event: CaptureEvent) => unknown;
|
|
13
13
|
/** Override the `fetch` implementation. */
|
|
14
14
|
fetchImpl?: typeof fetch;
|
|
15
|
+
/** Abort the capture after this many milliseconds. Defaults to 3000. */
|
|
16
|
+
timeoutMs?: number;
|
|
15
17
|
}
|
|
16
18
|
/**
|
|
17
19
|
* Adapter that POSTs each event to an arbitrary webhook URL. Keeps the
|
package/dist/adapters/webhook.js
CHANGED
|
@@ -1,22 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
function webhookAnalytics(config) {
|
|
3
|
-
const fetchImpl = config.fetchImpl ?? fetch;
|
|
4
|
-
const transform = config.transform ?? ((e) => e);
|
|
5
|
-
return {
|
|
6
|
-
async capture(event) {
|
|
7
|
-
await fetchImpl(config.url, {
|
|
8
|
-
method: "POST",
|
|
9
|
-
headers: {
|
|
10
|
-
"Content-Type": "application/json",
|
|
11
|
-
...config.headers ?? {}
|
|
12
|
-
},
|
|
13
|
-
body: JSON.stringify(transform(event)),
|
|
14
|
-
keepalive: true
|
|
15
|
-
});
|
|
16
|
-
}
|
|
17
|
-
};
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
export { webhookAnalytics };
|
|
21
|
-
//# sourceMappingURL=webhook.js.map
|
|
1
|
+
var n=class extends Error{status;body;constructor(s,o,e){super(s),this.name="CaptureTransportError",this.status=o,this.body=e;}};function u(t){let s=t.fetchImpl??fetch,o=t.transform??(e=>e);return {async capture(e){let r=await s(t.url,{method:"POST",headers:{"Content-Type":"application/json",...t.headers??{}},body:JSON.stringify(o(e)),keepalive:true,signal:AbortSignal.timeout(t.timeoutMs??3e3)});if(!r.ok)throw new n(`Webhook capture failed: ${r.status} ${r.statusText}`,r.status,await r.text().catch(()=>{}))}}}export{u as webhookAnalytics};//# sourceMappingURL=webhook.js.map
|
|
22
2
|
//# sourceMappingURL=webhook.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/adapters/webhook.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"sources":["../../src/errors.ts","../../src/adapters/webhook.ts"],"names":["CaptureTransportError","message","status","body","webhookAnalytics","config","fetchImpl","transform","event","res"],"mappings":"AACO,IAAMA,CAAAA,CAAN,cAAoC,KAAM,CACtC,MAAA,CACA,KACT,WAAA,CAAYC,CAAAA,CAAiBC,CAAAA,CAAiBC,CAAAA,CAAe,CAC3D,KAAA,CAAMF,CAAO,CAAA,CACb,IAAA,CAAK,IAAA,CAAO,uBAAA,CACZ,IAAA,CAAK,MAAA,CAASC,EACd,IAAA,CAAK,IAAA,CAAOC,EACd,CACF,CAAA,CCeO,SAASC,EAAiBC,CAAAA,CAAgD,CAC/E,IAAMC,CAAAA,CAAYD,CAAAA,CAAO,SAAA,EAAa,MAChCE,CAAAA,CAAYF,CAAAA,CAAO,SAAA,GAAe,CAAA,EAA6B,CAAA,CAAA,CAErE,OAAO,CACL,MAAM,OAAA,CAAQG,CAAAA,CAAoC,CAChD,IAAMC,CAAAA,CAAM,MAAMH,CAAAA,CAAUD,CAAAA,CAAO,GAAA,CAAK,CACtC,MAAA,CAAQ,MAAA,CACR,QAAS,CACP,cAAA,CAAgB,kBAAA,CAChB,GAAIA,CAAAA,CAAO,OAAA,EAAW,EACxB,CAAA,CACA,IAAA,CAAM,IAAA,CAAK,SAAA,CAAUE,CAAAA,CAAUC,CAAK,CAAC,CAAA,CACrC,SAAA,CAAW,IAAA,CACX,MAAA,CAAQ,WAAA,CAAY,QAAQH,CAAAA,CAAO,SAAA,EAAa,GAAI,CACtD,CAAC,CAAA,CACD,GAAI,CAACI,CAAAA,CAAI,EAAA,CACP,MAAM,IAAIT,CAAAA,CACR,2BAA2BS,CAAAA,CAAI,MAAM,CAAA,CAAA,EAAIA,CAAAA,CAAI,UAAU,CAAA,CAAA,CACvDA,EAAI,MAAA,CACJ,MAAMA,CAAAA,CAAI,IAAA,EAAK,CAAE,KAAA,CAAM,IAAG,CAAA,CAAY,CACxC,CAEJ,CACF,CACF","file":"webhook.js","sourcesContent":["/** Thrown when the analytics backend rejects, errors, or times out a capture. */\nexport class CaptureTransportError extends Error {\n readonly status: number | undefined\n readonly body: string | undefined\n constructor(message: string, status?: number, body?: string) {\n super(message)\n this.name = 'CaptureTransportError'\n this.status = status\n this.body = body\n }\n}\n","import type { AnalyticsAdapter, CaptureEvent } from '../types.js'\nimport { CaptureTransportError } from '../errors.js'\n\nexport interface WebhookAdapterConfig {\n /** Destination URL that receives a POST for each event. */\n url: string\n /** Extra headers merged onto the POST (useful for shared-secret auth). */\n headers?: Record<string, string>\n /**\n * Transform the event into the exact JSON body the destination expects.\n * Defaults to sending the {@link CaptureEvent} as-is.\n */\n transform?: (event: CaptureEvent) => unknown\n /** Override the `fetch` implementation. */\n fetchImpl?: typeof fetch\n /** Abort the capture after this many milliseconds. Defaults to 3000. */\n timeoutMs?: number\n}\n\n/**\n * Adapter that POSTs each event to an arbitrary webhook URL. Keeps the\n * library analytics-backend-agnostic — use this when PostHog isn't your\n * analytics of record, or when you want to multiplex events through your\n * own ingestion layer.\n */\nexport function webhookAnalytics(config: WebhookAdapterConfig): AnalyticsAdapter {\n const fetchImpl = config.fetchImpl ?? fetch\n const transform = config.transform ?? ((e: CaptureEvent): unknown => e)\n\n return {\n async capture(event: CaptureEvent): Promise<void> {\n const res = await fetchImpl(config.url, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n ...(config.headers ?? {})\n },\n body: JSON.stringify(transform(event)),\n keepalive: true,\n signal: AbortSignal.timeout(config.timeoutMs ?? 3000)\n })\n if (!res.ok) {\n throw new CaptureTransportError(\n `Webhook capture failed: ${res.status} ${res.statusText}`,\n res.status,\n await res.text().catch(() => undefined)\n )\n }\n }\n }\n}\n"]}
|