Replicates the nuxt-fathom one-liner for stx: add the plugin, give it an appId, done.
// stx.config.ts
export default {
plugins: ['@stacksjs/ts-analytics'],
analytics: { appId: 'YOUR_APP_ID' }, // apiEndpoint defaults to /api/analytics
}Injects the SPA-aware tracker before </head> on every SSR page (pageviews on first load and <StxLink> navigations via the history.pushState patch).
What's in here
The plugin (src/integrations/stx.ts, the package default export so the bare specifier resolves). Reads appId from the top-level analytics key (nuxt-fathom style) or inline plugin options, and adapts it into stx's native single-driver analytics (enabled + driver:'custom' + custom.inlineScript) so injection runs via injectAnalytics — after stx's client-script pass. Because stx re-wraps + re-runs that inline script on SPA nav, the if(!window.__tsAnalytics) guard is load-bearing.
Library build fix (blocker). build.ts was generateStaticSite({ outputDir: 'dist' }) — so dist/ was an SSG website with no dist/index.js, even though module/exports/types point there. The package wasn't importable and the plugin couldn't load. build.ts now does a real Bun.build (+ bun-plugin-dtsx) → dist/index.js + declarations; the dashboard site moves to bun run build:site → dist-site/ (preview.ts follows). The lambda deploy is unaffected (it uses scripts/build-views.ts).
Review hardening. JSON.stringify siteId/apiEndpoint in the tracker config (was raw single-quote interpolation — a quote/</script>/newline breakout for every generateFullTrackingScript consumer, the one path stx didn't already escape); validate appId/apiEndpoint (warn-and-skip); trailing-slash normalize; siteId strip; self-tagged idempotent re-run.
Verification
bun run build→dist/index.js(321 KB) +dist/index.d.ts; the built package's default export is the plugin (name: 'ts-analytics'+setup), namedstxPlugin+ all platform exports intact.test/stx-integration.test.ts— 7 cases green against the installed@stacksjs/stx.- Existing analytics tests: 188 pass / 0 fail (the escaping change is format-compatible).
Notes for reviewers
- The plugin is the package's first default export — non-stx consumers should use named exports. Documented in the README.
- It owns the
analyticskey and backs off if you've set a nativedriver. - The tracker POSTs to
${apiEndpoint}/collect— mount a collector or point at a hosted instance (README note). - Pairs with stacksjs/stx (bun-plugin serve path) — separate PR so the tracker also injects on
stx-serve.
🤖 Generated with Claude Code