@serge-ai/react 0.2.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/LICENSE +21 -0
- package/README.md +95 -0
- package/dist/index.cjs +4 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +57 -0
- package/dist/index.d.ts +57 -0
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -0
- package/package.json +71 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Superstellar LLC
|
|
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,95 @@
|
|
|
1
|
+
# @serge-ai/react
|
|
2
|
+
|
|
3
|
+
> **No cookies. No localStorage by default. No consent banner required.** ePrivacy Art. 5(3) compliant by construction.
|
|
4
|
+
|
|
5
|
+
React integration for [Serge](https://serge.ai) — drop-in `<Analytics />` and `<DetectionPixel />` components for catching AI-agent sessions on your site.
|
|
6
|
+
|
|
7
|
+
```sh
|
|
8
|
+
pnpm add @serge-ai/react
|
|
9
|
+
# or
|
|
10
|
+
npm install @serge-ai/react
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
For Next.js App Router consumers → use **[`@serge-ai/nextjs`](https://www.npmjs.com/package/@serge-ai/nextjs)** instead — it auto-tracks route changes via `usePathname` / `useSearchParams`.
|
|
14
|
+
|
|
15
|
+
For vanilla JS / Vue / Svelte → use **[`@serge-ai/js`](https://www.npmjs.com/package/@serge-ai/js)**.
|
|
16
|
+
|
|
17
|
+
## Quick start
|
|
18
|
+
|
|
19
|
+
```tsx
|
|
20
|
+
import { Analytics, DetectionPixel, track } from '@serge-ai/react'
|
|
21
|
+
|
|
22
|
+
export function App() {
|
|
23
|
+
return (
|
|
24
|
+
<>
|
|
25
|
+
<Analytics token="srg_pub_xxxxxxxxxxxxxxxxxxxxxxxxxx" />
|
|
26
|
+
<DetectionPixel token="srg_pub_xxxxxxxxxxxxxxxxxxxxxxxxxx" />
|
|
27
|
+
<button onClick={() => track('signup_clicked')}>Sign up</button>
|
|
28
|
+
</>
|
|
29
|
+
)
|
|
30
|
+
}
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
`<Analytics />` catches JS-driven agents (browser extensions, browser-use, Operator, ChatGPT Atlas, Claude Computer Use). `<DetectionPixel />` catches HTML-only agents that don't execute JS (ChatGPT-User, Claude-User, GPTBot, ClaudeBot, PerplexityBot). The pixel is what makes Serge see traffic that PostHog, GA4, and Hotjar miss entirely.
|
|
34
|
+
|
|
35
|
+
## Components
|
|
36
|
+
|
|
37
|
+
### `<Analytics />`
|
|
38
|
+
|
|
39
|
+
Drop-in component. Calls `init()` once on mount, returns `null`.
|
|
40
|
+
|
|
41
|
+
```tsx
|
|
42
|
+
<Analytics
|
|
43
|
+
token="srg_pub_xxx"
|
|
44
|
+
apiBase="https://serge.ai" // optional first-party proxy override
|
|
45
|
+
debug={false} // optional
|
|
46
|
+
requireConsent={false} // optional
|
|
47
|
+
/>
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Re-renders only re-call `init()` if the `token` changes. Other config edits require a remount — the tracker's runtime state is module-level and changing flags mid-session produces unpredictable behavior.
|
|
51
|
+
|
|
52
|
+
### `<DetectionPixel />`
|
|
53
|
+
|
|
54
|
+
Renders an invisible 1×1 GIF that catches non-JS agents. Drop next to `<Analytics />`.
|
|
55
|
+
|
|
56
|
+
```tsx
|
|
57
|
+
<DetectionPixel
|
|
58
|
+
token="srg_pub_xxx"
|
|
59
|
+
apiBase="https://serge.ai" // optional
|
|
60
|
+
/>
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
The image fetch fires from the agent direct to the Serge ingest endpoint with the agent's `User-Agent` intact. Serge's server matches the UA against known agent patterns (ChatGPT-User, Claude-User, GPTBot, ClaudeBot, PerplexityBot, …), validates the `Referer` against your registered domain, then writes the hit to your dashboard. **No competing analytics SDK ships this.**
|
|
64
|
+
|
|
65
|
+
## API re-exports
|
|
66
|
+
|
|
67
|
+
```tsx
|
|
68
|
+
import { init, track, consent, pageview } from '@serge-ai/react'
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
These are re-exports from `@serge-ai/js` so consumers can import everything from one path. See **[`@serge-ai/js` docs](https://www.npmjs.com/package/@serge-ai/js)** for the full reference.
|
|
72
|
+
|
|
73
|
+
## SSR safety
|
|
74
|
+
|
|
75
|
+
Every browser-API access is guarded inside `useEffect`. Components are marked `'use client'`. Safe to import from server components, RSC layouts, Edge runtimes.
|
|
76
|
+
|
|
77
|
+
## Why no `<TrackerProvider>` / `useTracker()` hook?
|
|
78
|
+
|
|
79
|
+
PostHog tried both patterns; the convergent industry pattern is the singleton `init()` + import-anywhere model (Sentry, Mixpanel, Amplitude, Segment all do this). The provider+hook adds context-render overhead without giving the consumer anything they couldn't get from the singleton import. We dropped them in 0.2.0.
|
|
80
|
+
|
|
81
|
+
## Why no `identify()`?
|
|
82
|
+
|
|
83
|
+
Serge measures **agent sessions**, not end-users. Per-user identity is anti-goal for cookieless agent analytics. See `@serge-ai/js` README for the reasoning.
|
|
84
|
+
|
|
85
|
+
## React versions
|
|
86
|
+
|
|
87
|
+
`react@^18 || ^19` (peer dependency). Works in both 18 and 19.
|
|
88
|
+
|
|
89
|
+
## Bundle size
|
|
90
|
+
|
|
91
|
+
Under 6 KB gzipped (excluding `react` and `@serge-ai/js`, which the host already provides). [`size-limit`](https://github.com/ai/size-limit) enforces this in CI.
|
|
92
|
+
|
|
93
|
+
## License
|
|
94
|
+
|
|
95
|
+
MIT © Superstellar LLC. See [LICENSE](./LICENSE).
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
'use strict';var react=require('react'),js=require('@serge-ai/js'),jsxRuntime=require('react/jsx-runtime');function u({token:e,...t}){return react.useEffect(()=>{js.init(e,t);},[e]),null}var r={position:"absolute",width:1,height:1,opacity:0,pointerEvents:"none",border:"none"};function y({token:e,apiBase:t}){let n=`${(t??"https://serge.ai").replace(/\/$/,"")}/api/ingest/pixel?s=${encodeURIComponent(e)}`;return jsxRuntime.jsx("img",{src:n,width:1,height:1,alt:"","aria-hidden":"true",loading:"eager",decoding:"async",referrerPolicy:"no-referrer-when-downgrade",style:r})}
|
|
3
|
+
Object.defineProperty(exports,"consent",{enumerable:true,get:function(){return js.consent}});Object.defineProperty(exports,"init",{enumerable:true,get:function(){return js.init}});Object.defineProperty(exports,"pageview",{enumerable:true,get:function(){return js.pageview}});Object.defineProperty(exports,"track",{enumerable:true,get:function(){return js.track}});exports.Analytics=u;exports.DetectionPixel=y;//# sourceMappingURL=index.cjs.map
|
|
4
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.tsx"],"names":["Analytics","token","options","useEffect","init","PIXEL_STYLE","DetectionPixel","apiBase","src","jsx"],"mappings":"2GAwEO,SAASA,EAAU,CAAE,KAAA,CAAAC,CAAAA,CAAO,GAAGC,CAAQ,CAAA,CAAyB,CACrE,OAAAC,eAAAA,CAAU,IAAM,CACdC,OAAAA,CAAKH,EAAOC,CAAO,EAErB,CAAA,CAAG,CAACD,CAAK,CAAC,EAEH,IACT,CAkCA,IAAMI,CAAAA,CAA6B,CACjC,SAAU,UAAA,CACV,KAAA,CAAO,CAAA,CACP,MAAA,CAAQ,CAAA,CACR,OAAA,CAAS,EACT,aAAA,CAAe,MAAA,CACf,OAAQ,MACV,CAAA,CAEO,SAASC,CAAAA,CAAe,CAAE,KAAA,CAAAL,CAAAA,CAAO,OAAA,CAAAM,CAAQ,EAA2C,CAEzF,IAAMC,EAAM,CAAA,EAAA,CADED,CAAAA,EAAW,oBAAoB,OAAA,CAAQ,KAAA,CAAO,EAAE,CAC3C,CAAA,oBAAA,EAAuB,kBAAA,CAAmBN,CAAK,CAAC,CAAA,CAAA,CACnE,OAEEQ,cAAAA,CAAC,KAAA,CAAA,CACC,IAAKD,CAAAA,CACL,KAAA,CAAO,CAAA,CACP,MAAA,CAAQ,CAAA,CACR,GAAA,CAAI,GACJ,aAAA,CAAY,MAAA,CACZ,QAAQ,OAAA,CACR,QAAA,CAAS,QACT,cAAA,CAAe,4BAAA,CACf,KAAA,CAAOH,CAAAA,CACT,CAEJ","file":"index.cjs","sourcesContent":["/**\n * @serge-ai/react — React integration.\n *\n * Two components, both safe for SSR (no module-level browser access,\n * all side effects inside `useEffect`):\n *\n * 1. `<Analytics />` — drop in once at the root of your app to capture\n * JS-driven agent sessions (snippet equivalent):\n *\n * import { Analytics } from '@serge-ai/react'\n * export default function App() {\n * return (\n * <>\n * <Analytics token=\"srg_pub_xxx\" />\n * <YourApp />\n * </>\n * )\n * }\n *\n * 2. `<DetectionPixel />` — drop next to `<Analytics />` to also catch\n * non-JS agents (ChatGPT-User, Claude-User, GPTBot, ClaudeBot,\n * PerplexityBot — agents that fetch HTML and follow `<img>` but\n * don't execute JS). Renders an invisible 1×1 GIF pointing at our\n * server. The image fetch fires from the agent direct to serge.ai\n * with the agent's User-Agent intact:\n *\n * <Analytics token=\"srg_pub_xxx\" />\n * <DetectionPixel token=\"srg_pub_xxx\" />\n *\n * PostHog has no equivalent — JS-only collection is the industry\n * standard. Serge ships the pixel on purpose because non-JS agents\n * are a meaningful and growing share of agent traffic.\n *\n * Re-exports the core `track` / `consent` / `pageview` so consumers\n * can import everything from the React subpath if they prefer one\n * import path. (`identify` is intentionally NOT exported — Serge\n * classifies sessions, not end-users; per-user identity is anti-goal\n * for cookieless agent analytics.)\n */\n\n'use client'\n\nimport { useEffect, type CSSProperties } from 'react'\nimport {\n init,\n track,\n consent,\n pageview,\n type InitOptions,\n type ConsentValue,\n} from '@serge-ai/js'\n\nexport { init, track, consent, pageview }\nexport type { InitOptions, ConsentValue }\n\n/* ── <Analytics /> ────────────────────────────────────────────────\n *\n * Calls `init()` once on mount with the props as the token + options,\n * returns nothing renderable. Same shape as @vercel/analytics' drop-in\n * pattern — match it exactly so consumers swapping between SDKs see\n * the same component contract.\n *\n * Re-renders only re-call `init()` if the token changes. Other config\n * edits (debug, requireConsent) require a remount — the tracker's\n * runtime state is module-level, and changing flags mid-session\n * produces unpredictable behavior. */\n\nexport interface AnalyticsProps extends InitOptions {\n /** Public token from the Serge dashboard (`srg_pub_*`). */\n token: string\n}\n\nexport function Analytics({ token, ...options }: AnalyticsProps): null {\n useEffect(() => {\n init(token, options)\n /* eslint-disable-next-line react-hooks/exhaustive-deps -- intentional: only token triggers re-init */\n }, [token])\n\n return null\n}\n\n/* ── <DetectionPixel /> ───────────────────────────────────────────\n *\n * Renders a 1×1 invisible `<img>` whose `src` points at the Serge\n * pixel ingest endpoint. Non-JS agents (HTML-only crawlers) follow\n * the `<img>` reference and trigger the request — Serge captures\n * their User-Agent and records the visit.\n *\n * Implementation notes:\n * - The image is positioned absolutely with size-1 + opacity-0 so\n * it doesn't take any visible space or affect layout.\n * - `aria-hidden=\"true\"` + empty `alt=\"\"` keeps it invisible to\n * assistive tech.\n * - `loading=\"eager\"` so it fires immediately on render — lazy\n * loading would defeat the detection purpose entirely.\n * - `referrerPolicy=\"no-referrer-when-downgrade\"` matches what\n * serge.ai itself uses; lets the server validate the Referer\n * against the registered domain.\n *\n * Pre-existing endpoint at /api/ingest/pixel — see\n * app/api/ingest/pixel/route.ts. Server-side validation: site_id\n * shape, UA matched against known agent regex, per-IP+per-site rate\n * limit, Referer-host vs registered domain. The customer's site only\n * needs to be in their workspace's registered domains for hits to\n * land in their dashboard. */\n\nexport interface DetectionPixelProps {\n /** Public token from the Serge dashboard (`srg_pub_*`). */\n token: string\n /** Origin of the Serge ingest API. Defaults to `https://serge.ai`. */\n apiBase?: string\n}\n\nconst PIXEL_STYLE: CSSProperties = {\n position: 'absolute',\n width: 1,\n height: 1,\n opacity: 0,\n pointerEvents: 'none',\n border: 'none',\n}\n\nexport function DetectionPixel({ token, apiBase }: DetectionPixelProps): React.JSX.Element {\n const base = (apiBase ?? 'https://serge.ai').replace(/\\/$/, '')\n const src = `${base}/api/ingest/pixel?s=${encodeURIComponent(token)}`\n return (\n /* eslint-disable-next-line @next/next/no-img-element -- a tracking pixel must be a literal <img>; alt=\"\" makes it intentionally decorative. */\n <img\n src={src}\n width={1}\n height={1}\n alt=\"\"\n aria-hidden=\"true\"\n loading=\"eager\"\n decoding=\"async\"\n referrerPolicy=\"no-referrer-when-downgrade\"\n style={PIXEL_STYLE}\n />\n )\n}\n"]}
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { InitOptions } from '@serge-ai/js';
|
|
2
|
+
export { ConsentValue, InitOptions, consent, init, pageview, track } from '@serge-ai/js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* @serge-ai/react — React integration.
|
|
6
|
+
*
|
|
7
|
+
* Two components, both safe for SSR (no module-level browser access,
|
|
8
|
+
* all side effects inside `useEffect`):
|
|
9
|
+
*
|
|
10
|
+
* 1. `<Analytics />` — drop in once at the root of your app to capture
|
|
11
|
+
* JS-driven agent sessions (snippet equivalent):
|
|
12
|
+
*
|
|
13
|
+
* import { Analytics } from '@serge-ai/react'
|
|
14
|
+
* export default function App() {
|
|
15
|
+
* return (
|
|
16
|
+
* <>
|
|
17
|
+
* <Analytics token="srg_pub_xxx" />
|
|
18
|
+
* <YourApp />
|
|
19
|
+
* </>
|
|
20
|
+
* )
|
|
21
|
+
* }
|
|
22
|
+
*
|
|
23
|
+
* 2. `<DetectionPixel />` — drop next to `<Analytics />` to also catch
|
|
24
|
+
* non-JS agents (ChatGPT-User, Claude-User, GPTBot, ClaudeBot,
|
|
25
|
+
* PerplexityBot — agents that fetch HTML and follow `<img>` but
|
|
26
|
+
* don't execute JS). Renders an invisible 1×1 GIF pointing at our
|
|
27
|
+
* server. The image fetch fires from the agent direct to serge.ai
|
|
28
|
+
* with the agent's User-Agent intact:
|
|
29
|
+
*
|
|
30
|
+
* <Analytics token="srg_pub_xxx" />
|
|
31
|
+
* <DetectionPixel token="srg_pub_xxx" />
|
|
32
|
+
*
|
|
33
|
+
* PostHog has no equivalent — JS-only collection is the industry
|
|
34
|
+
* standard. Serge ships the pixel on purpose because non-JS agents
|
|
35
|
+
* are a meaningful and growing share of agent traffic.
|
|
36
|
+
*
|
|
37
|
+
* Re-exports the core `track` / `consent` / `pageview` so consumers
|
|
38
|
+
* can import everything from the React subpath if they prefer one
|
|
39
|
+
* import path. (`identify` is intentionally NOT exported — Serge
|
|
40
|
+
* classifies sessions, not end-users; per-user identity is anti-goal
|
|
41
|
+
* for cookieless agent analytics.)
|
|
42
|
+
*/
|
|
43
|
+
|
|
44
|
+
interface AnalyticsProps extends InitOptions {
|
|
45
|
+
/** Public token from the Serge dashboard (`srg_pub_*`). */
|
|
46
|
+
token: string;
|
|
47
|
+
}
|
|
48
|
+
declare function Analytics({ token, ...options }: AnalyticsProps): null;
|
|
49
|
+
interface DetectionPixelProps {
|
|
50
|
+
/** Public token from the Serge dashboard (`srg_pub_*`). */
|
|
51
|
+
token: string;
|
|
52
|
+
/** Origin of the Serge ingest API. Defaults to `https://serge.ai`. */
|
|
53
|
+
apiBase?: string;
|
|
54
|
+
}
|
|
55
|
+
declare function DetectionPixel({ token, apiBase }: DetectionPixelProps): React.JSX.Element;
|
|
56
|
+
|
|
57
|
+
export { Analytics, type AnalyticsProps, DetectionPixel, type DetectionPixelProps };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { InitOptions } from '@serge-ai/js';
|
|
2
|
+
export { ConsentValue, InitOptions, consent, init, pageview, track } from '@serge-ai/js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* @serge-ai/react — React integration.
|
|
6
|
+
*
|
|
7
|
+
* Two components, both safe for SSR (no module-level browser access,
|
|
8
|
+
* all side effects inside `useEffect`):
|
|
9
|
+
*
|
|
10
|
+
* 1. `<Analytics />` — drop in once at the root of your app to capture
|
|
11
|
+
* JS-driven agent sessions (snippet equivalent):
|
|
12
|
+
*
|
|
13
|
+
* import { Analytics } from '@serge-ai/react'
|
|
14
|
+
* export default function App() {
|
|
15
|
+
* return (
|
|
16
|
+
* <>
|
|
17
|
+
* <Analytics token="srg_pub_xxx" />
|
|
18
|
+
* <YourApp />
|
|
19
|
+
* </>
|
|
20
|
+
* )
|
|
21
|
+
* }
|
|
22
|
+
*
|
|
23
|
+
* 2. `<DetectionPixel />` — drop next to `<Analytics />` to also catch
|
|
24
|
+
* non-JS agents (ChatGPT-User, Claude-User, GPTBot, ClaudeBot,
|
|
25
|
+
* PerplexityBot — agents that fetch HTML and follow `<img>` but
|
|
26
|
+
* don't execute JS). Renders an invisible 1×1 GIF pointing at our
|
|
27
|
+
* server. The image fetch fires from the agent direct to serge.ai
|
|
28
|
+
* with the agent's User-Agent intact:
|
|
29
|
+
*
|
|
30
|
+
* <Analytics token="srg_pub_xxx" />
|
|
31
|
+
* <DetectionPixel token="srg_pub_xxx" />
|
|
32
|
+
*
|
|
33
|
+
* PostHog has no equivalent — JS-only collection is the industry
|
|
34
|
+
* standard. Serge ships the pixel on purpose because non-JS agents
|
|
35
|
+
* are a meaningful and growing share of agent traffic.
|
|
36
|
+
*
|
|
37
|
+
* Re-exports the core `track` / `consent` / `pageview` so consumers
|
|
38
|
+
* can import everything from the React subpath if they prefer one
|
|
39
|
+
* import path. (`identify` is intentionally NOT exported — Serge
|
|
40
|
+
* classifies sessions, not end-users; per-user identity is anti-goal
|
|
41
|
+
* for cookieless agent analytics.)
|
|
42
|
+
*/
|
|
43
|
+
|
|
44
|
+
interface AnalyticsProps extends InitOptions {
|
|
45
|
+
/** Public token from the Serge dashboard (`srg_pub_*`). */
|
|
46
|
+
token: string;
|
|
47
|
+
}
|
|
48
|
+
declare function Analytics({ token, ...options }: AnalyticsProps): null;
|
|
49
|
+
interface DetectionPixelProps {
|
|
50
|
+
/** Public token from the Serge dashboard (`srg_pub_*`). */
|
|
51
|
+
token: string;
|
|
52
|
+
/** Origin of the Serge ingest API. Defaults to `https://serge.ai`. */
|
|
53
|
+
apiBase?: string;
|
|
54
|
+
}
|
|
55
|
+
declare function DetectionPixel({ token, apiBase }: DetectionPixelProps): React.JSX.Element;
|
|
56
|
+
|
|
57
|
+
export { Analytics, type AnalyticsProps, DetectionPixel, type DetectionPixelProps };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import {useEffect}from'react';import {init}from'@serge-ai/js';export{consent,init,pageview,track}from'@serge-ai/js';import {jsx}from'react/jsx-runtime';function u({token:e,...t}){return useEffect(()=>{init(e,t);},[e]),null}var r={position:"absolute",width:1,height:1,opacity:0,pointerEvents:"none",border:"none"};function y({token:e,apiBase:t}){let n=`${(t??"https://serge.ai").replace(/\/$/,"")}/api/ingest/pixel?s=${encodeURIComponent(e)}`;return jsx("img",{src:n,width:1,height:1,alt:"","aria-hidden":"true",loading:"eager",decoding:"async",referrerPolicy:"no-referrer-when-downgrade",style:r})}
|
|
3
|
+
export{u as Analytics,y as DetectionPixel};//# sourceMappingURL=index.js.map
|
|
4
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.tsx"],"names":["Analytics","token","options","useEffect","init","PIXEL_STYLE","DetectionPixel","apiBase","src","jsx"],"mappings":"wJAwEO,SAASA,EAAU,CAAE,KAAA,CAAAC,CAAAA,CAAO,GAAGC,CAAQ,CAAA,CAAyB,CACrE,OAAAC,SAAAA,CAAU,IAAM,CACdC,IAAAA,CAAKH,EAAOC,CAAO,EAErB,CAAA,CAAG,CAACD,CAAK,CAAC,EAEH,IACT,CAkCA,IAAMI,CAAAA,CAA6B,CACjC,SAAU,UAAA,CACV,KAAA,CAAO,CAAA,CACP,MAAA,CAAQ,CAAA,CACR,OAAA,CAAS,EACT,aAAA,CAAe,MAAA,CACf,OAAQ,MACV,CAAA,CAEO,SAASC,CAAAA,CAAe,CAAE,KAAA,CAAAL,CAAAA,CAAO,OAAA,CAAAM,CAAQ,EAA2C,CAEzF,IAAMC,EAAM,CAAA,EAAA,CADED,CAAAA,EAAW,oBAAoB,OAAA,CAAQ,KAAA,CAAO,EAAE,CAC3C,CAAA,oBAAA,EAAuB,kBAAA,CAAmBN,CAAK,CAAC,CAAA,CAAA,CACnE,OAEEQ,GAAAA,CAAC,KAAA,CAAA,CACC,IAAKD,CAAAA,CACL,KAAA,CAAO,CAAA,CACP,MAAA,CAAQ,CAAA,CACR,GAAA,CAAI,GACJ,aAAA,CAAY,MAAA,CACZ,QAAQ,OAAA,CACR,QAAA,CAAS,QACT,cAAA,CAAe,4BAAA,CACf,KAAA,CAAOH,CAAAA,CACT,CAEJ","file":"index.js","sourcesContent":["/**\n * @serge-ai/react — React integration.\n *\n * Two components, both safe for SSR (no module-level browser access,\n * all side effects inside `useEffect`):\n *\n * 1. `<Analytics />` — drop in once at the root of your app to capture\n * JS-driven agent sessions (snippet equivalent):\n *\n * import { Analytics } from '@serge-ai/react'\n * export default function App() {\n * return (\n * <>\n * <Analytics token=\"srg_pub_xxx\" />\n * <YourApp />\n * </>\n * )\n * }\n *\n * 2. `<DetectionPixel />` — drop next to `<Analytics />` to also catch\n * non-JS agents (ChatGPT-User, Claude-User, GPTBot, ClaudeBot,\n * PerplexityBot — agents that fetch HTML and follow `<img>` but\n * don't execute JS). Renders an invisible 1×1 GIF pointing at our\n * server. The image fetch fires from the agent direct to serge.ai\n * with the agent's User-Agent intact:\n *\n * <Analytics token=\"srg_pub_xxx\" />\n * <DetectionPixel token=\"srg_pub_xxx\" />\n *\n * PostHog has no equivalent — JS-only collection is the industry\n * standard. Serge ships the pixel on purpose because non-JS agents\n * are a meaningful and growing share of agent traffic.\n *\n * Re-exports the core `track` / `consent` / `pageview` so consumers\n * can import everything from the React subpath if they prefer one\n * import path. (`identify` is intentionally NOT exported — Serge\n * classifies sessions, not end-users; per-user identity is anti-goal\n * for cookieless agent analytics.)\n */\n\n'use client'\n\nimport { useEffect, type CSSProperties } from 'react'\nimport {\n init,\n track,\n consent,\n pageview,\n type InitOptions,\n type ConsentValue,\n} from '@serge-ai/js'\n\nexport { init, track, consent, pageview }\nexport type { InitOptions, ConsentValue }\n\n/* ── <Analytics /> ────────────────────────────────────────────────\n *\n * Calls `init()` once on mount with the props as the token + options,\n * returns nothing renderable. Same shape as @vercel/analytics' drop-in\n * pattern — match it exactly so consumers swapping between SDKs see\n * the same component contract.\n *\n * Re-renders only re-call `init()` if the token changes. Other config\n * edits (debug, requireConsent) require a remount — the tracker's\n * runtime state is module-level, and changing flags mid-session\n * produces unpredictable behavior. */\n\nexport interface AnalyticsProps extends InitOptions {\n /** Public token from the Serge dashboard (`srg_pub_*`). */\n token: string\n}\n\nexport function Analytics({ token, ...options }: AnalyticsProps): null {\n useEffect(() => {\n init(token, options)\n /* eslint-disable-next-line react-hooks/exhaustive-deps -- intentional: only token triggers re-init */\n }, [token])\n\n return null\n}\n\n/* ── <DetectionPixel /> ───────────────────────────────────────────\n *\n * Renders a 1×1 invisible `<img>` whose `src` points at the Serge\n * pixel ingest endpoint. Non-JS agents (HTML-only crawlers) follow\n * the `<img>` reference and trigger the request — Serge captures\n * their User-Agent and records the visit.\n *\n * Implementation notes:\n * - The image is positioned absolutely with size-1 + opacity-0 so\n * it doesn't take any visible space or affect layout.\n * - `aria-hidden=\"true\"` + empty `alt=\"\"` keeps it invisible to\n * assistive tech.\n * - `loading=\"eager\"` so it fires immediately on render — lazy\n * loading would defeat the detection purpose entirely.\n * - `referrerPolicy=\"no-referrer-when-downgrade\"` matches what\n * serge.ai itself uses; lets the server validate the Referer\n * against the registered domain.\n *\n * Pre-existing endpoint at /api/ingest/pixel — see\n * app/api/ingest/pixel/route.ts. Server-side validation: site_id\n * shape, UA matched against known agent regex, per-IP+per-site rate\n * limit, Referer-host vs registered domain. The customer's site only\n * needs to be in their workspace's registered domains for hits to\n * land in their dashboard. */\n\nexport interface DetectionPixelProps {\n /** Public token from the Serge dashboard (`srg_pub_*`). */\n token: string\n /** Origin of the Serge ingest API. Defaults to `https://serge.ai`. */\n apiBase?: string\n}\n\nconst PIXEL_STYLE: CSSProperties = {\n position: 'absolute',\n width: 1,\n height: 1,\n opacity: 0,\n pointerEvents: 'none',\n border: 'none',\n}\n\nexport function DetectionPixel({ token, apiBase }: DetectionPixelProps): React.JSX.Element {\n const base = (apiBase ?? 'https://serge.ai').replace(/\\/$/, '')\n const src = `${base}/api/ingest/pixel?s=${encodeURIComponent(token)}`\n return (\n /* eslint-disable-next-line @next/next/no-img-element -- a tracking pixel must be a literal <img>; alt=\"\" makes it intentionally decorative. */\n <img\n src={src}\n width={1}\n height={1}\n alt=\"\"\n aria-hidden=\"true\"\n loading=\"eager\"\n decoding=\"async\"\n referrerPolicy=\"no-referrer-when-downgrade\"\n style={PIXEL_STYLE}\n />\n )\n}\n"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@serge-ai/react",
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "Serge for React — drop-in <Analytics /> + <DetectionPixel /> components for catching AI-agent sessions on your site.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"homepage": "https://serge.ai",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "https://github.com/SuperstellarLLC/heyserge.git",
|
|
10
|
+
"directory": "packages/react"
|
|
11
|
+
},
|
|
12
|
+
"bugs": {
|
|
13
|
+
"url": "https://github.com/SuperstellarLLC/heyserge/issues"
|
|
14
|
+
},
|
|
15
|
+
"keywords": [
|
|
16
|
+
"react",
|
|
17
|
+
"analytics",
|
|
18
|
+
"tracking",
|
|
19
|
+
"agent",
|
|
20
|
+
"ai-agent",
|
|
21
|
+
"ssr",
|
|
22
|
+
"cookieless"
|
|
23
|
+
],
|
|
24
|
+
"type": "module",
|
|
25
|
+
"main": "./dist/index.cjs",
|
|
26
|
+
"module": "./dist/index.js",
|
|
27
|
+
"types": "./dist/index.d.ts",
|
|
28
|
+
"sideEffects": false,
|
|
29
|
+
"exports": {
|
|
30
|
+
"./package.json": "./package.json",
|
|
31
|
+
".": {
|
|
32
|
+
"import": {
|
|
33
|
+
"types": "./dist/index.d.ts",
|
|
34
|
+
"default": "./dist/index.js"
|
|
35
|
+
},
|
|
36
|
+
"require": {
|
|
37
|
+
"types": "./dist/index.d.cts",
|
|
38
|
+
"default": "./dist/index.cjs"
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
"files": [
|
|
43
|
+
"dist",
|
|
44
|
+
"README.md",
|
|
45
|
+
"LICENSE"
|
|
46
|
+
],
|
|
47
|
+
"dependencies": {
|
|
48
|
+
"@serge-ai/js": "0.2.0"
|
|
49
|
+
},
|
|
50
|
+
"peerDependencies": {
|
|
51
|
+
"react": "^18 || ^19"
|
|
52
|
+
},
|
|
53
|
+
"devDependencies": {
|
|
54
|
+
"@size-limit/preset-small-lib": "^11.1.0",
|
|
55
|
+
"@types/react": "^19.0.0",
|
|
56
|
+
"react": "^19.0.0",
|
|
57
|
+
"size-limit": "^11.1.0",
|
|
58
|
+
"tsup": "^8.3.5",
|
|
59
|
+
"typescript": "^5.8.2",
|
|
60
|
+
"vitest": "^3.1.1"
|
|
61
|
+
},
|
|
62
|
+
"publishConfig": {
|
|
63
|
+
"access": "public"
|
|
64
|
+
},
|
|
65
|
+
"scripts": {
|
|
66
|
+
"build": "tsup",
|
|
67
|
+
"test": "vitest run --config ./vitest.config.ts --passWithNoTests",
|
|
68
|
+
"typecheck": "tsc --noEmit",
|
|
69
|
+
"size": "size-limit"
|
|
70
|
+
}
|
|
71
|
+
}
|