@flightdev/analytics 0.0.2
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 +363 -0
- package/dist/adapters/plausible.d.ts +13 -0
- package/dist/adapters/plausible.js +36 -0
- package/dist/adapters/posthog.d.ts +13 -0
- package/dist/adapters/posthog.js +44 -0
- package/dist/index.d.ts +49 -0
- package/dist/index.js +37 -0
- package/package.json +34 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024-2026 Flight Contributors
|
|
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,363 @@
|
|
|
1
|
+
# @flight-framework/analytics
|
|
2
|
+
|
|
3
|
+
Privacy-focused, universal analytics for Flight Framework. Works with any analytics provider through adapters.
|
|
4
|
+
|
|
5
|
+
## Table of Contents
|
|
6
|
+
|
|
7
|
+
- [Features](#features)
|
|
8
|
+
- [Installation](#installation)
|
|
9
|
+
- [Quick Start](#quick-start)
|
|
10
|
+
- [Adapters](#adapters)
|
|
11
|
+
- [Core API](#core-api)
|
|
12
|
+
- [React Integration](#react-integration)
|
|
13
|
+
- [Server-Side Tracking](#server-side-tracking)
|
|
14
|
+
- [Privacy and Consent](#privacy-and-consent)
|
|
15
|
+
- [API Reference](#api-reference)
|
|
16
|
+
- [License](#license)
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
## Features
|
|
21
|
+
|
|
22
|
+
- Single API for multiple analytics providers
|
|
23
|
+
- Privacy-first design with consent management
|
|
24
|
+
- Automatic page view tracking
|
|
25
|
+
- Server-side event tracking
|
|
26
|
+
- Type-safe event definitions
|
|
27
|
+
- Batching and retry logic
|
|
28
|
+
- React hooks for easy integration
|
|
29
|
+
- GDPR/CCPA compliant consent handling
|
|
30
|
+
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
## Installation
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
npm install @flight-framework/analytics
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
---
|
|
40
|
+
|
|
41
|
+
## Quick Start
|
|
42
|
+
|
|
43
|
+
```typescript
|
|
44
|
+
import { createAnalytics } from '@flight-framework/analytics';
|
|
45
|
+
import { plausible } from '@flight-framework/analytics/plausible';
|
|
46
|
+
|
|
47
|
+
const analytics = createAnalytics(plausible({
|
|
48
|
+
domain: 'example.com',
|
|
49
|
+
}));
|
|
50
|
+
|
|
51
|
+
// Track page view
|
|
52
|
+
analytics.trackPageview('/about');
|
|
53
|
+
|
|
54
|
+
// Track custom event
|
|
55
|
+
analytics.trackEvent('signup', { plan: 'pro', source: 'landing' });
|
|
56
|
+
|
|
57
|
+
// Identify user (for providers that support it)
|
|
58
|
+
analytics.identify('user_123', { email: 'user@example.com' });
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
---
|
|
62
|
+
|
|
63
|
+
## Adapters
|
|
64
|
+
|
|
65
|
+
### Plausible (Privacy-Focused)
|
|
66
|
+
|
|
67
|
+
Simple, privacy-friendly analytics without cookies.
|
|
68
|
+
|
|
69
|
+
```typescript
|
|
70
|
+
import { plausible } from '@flight-framework/analytics/plausible';
|
|
71
|
+
|
|
72
|
+
const adapter = plausible({
|
|
73
|
+
domain: 'example.com',
|
|
74
|
+
apiHost: 'https://plausible.io', // Or self-hosted
|
|
75
|
+
hashMode: false, // Hash-based routing
|
|
76
|
+
});
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### PostHog (Product Analytics)
|
|
80
|
+
|
|
81
|
+
Full-featured product analytics with session recording.
|
|
82
|
+
|
|
83
|
+
```typescript
|
|
84
|
+
import { posthog } from '@flight-framework/analytics/posthog';
|
|
85
|
+
|
|
86
|
+
const adapter = posthog({
|
|
87
|
+
apiKey: process.env.POSTHOG_API_KEY,
|
|
88
|
+
host: 'https://app.posthog.com', // Or self-hosted
|
|
89
|
+
autocapture: true,
|
|
90
|
+
sessionRecording: true,
|
|
91
|
+
});
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### Google Analytics 4
|
|
95
|
+
|
|
96
|
+
```typescript
|
|
97
|
+
import { ga4 } from '@flight-framework/analytics/ga4';
|
|
98
|
+
|
|
99
|
+
const adapter = ga4({
|
|
100
|
+
measurementId: 'G-XXXXXXXXXX',
|
|
101
|
+
debug: process.env.NODE_ENV === 'development',
|
|
102
|
+
});
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
### Mixpanel
|
|
106
|
+
|
|
107
|
+
```typescript
|
|
108
|
+
import { mixpanel } from '@flight-framework/analytics/mixpanel';
|
|
109
|
+
|
|
110
|
+
const adapter = mixpanel({
|
|
111
|
+
token: process.env.MIXPANEL_TOKEN,
|
|
112
|
+
debug: false,
|
|
113
|
+
});
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
### Amplitude
|
|
117
|
+
|
|
118
|
+
```typescript
|
|
119
|
+
import { amplitude } from '@flight-framework/analytics/amplitude';
|
|
120
|
+
|
|
121
|
+
const adapter = amplitude({
|
|
122
|
+
apiKey: process.env.AMPLITUDE_API_KEY,
|
|
123
|
+
serverZone: 'US', // or 'EU'
|
|
124
|
+
});
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
### Multiple Providers
|
|
128
|
+
|
|
129
|
+
Send events to multiple providers simultaneously:
|
|
130
|
+
|
|
131
|
+
```typescript
|
|
132
|
+
import { multi } from '@flight-framework/analytics/multi';
|
|
133
|
+
|
|
134
|
+
const analytics = createAnalytics(multi([
|
|
135
|
+
plausible({ domain: 'example.com' }),
|
|
136
|
+
posthog({ apiKey: '...' }),
|
|
137
|
+
]));
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
---
|
|
141
|
+
|
|
142
|
+
## Core API
|
|
143
|
+
|
|
144
|
+
### trackPageview(path, properties?)
|
|
145
|
+
|
|
146
|
+
Track a page view:
|
|
147
|
+
|
|
148
|
+
```typescript
|
|
149
|
+
analytics.trackPageview('/products/widget');
|
|
150
|
+
|
|
151
|
+
// With additional properties
|
|
152
|
+
analytics.trackPageview('/products/widget', {
|
|
153
|
+
referrer: document.referrer,
|
|
154
|
+
title: document.title,
|
|
155
|
+
});
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
### trackEvent(name, properties?)
|
|
159
|
+
|
|
160
|
+
Track a custom event:
|
|
161
|
+
|
|
162
|
+
```typescript
|
|
163
|
+
// Simple event
|
|
164
|
+
analytics.trackEvent('button_click');
|
|
165
|
+
|
|
166
|
+
// With properties
|
|
167
|
+
analytics.trackEvent('purchase', {
|
|
168
|
+
product_id: 'prod_123',
|
|
169
|
+
price: 49.99,
|
|
170
|
+
currency: 'USD',
|
|
171
|
+
});
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
### identify(userId, traits?)
|
|
175
|
+
|
|
176
|
+
Identify a user for user-level analytics:
|
|
177
|
+
|
|
178
|
+
```typescript
|
|
179
|
+
analytics.identify('user_123', {
|
|
180
|
+
email: 'user@example.com',
|
|
181
|
+
plan: 'pro',
|
|
182
|
+
company: 'Acme Inc',
|
|
183
|
+
signupDate: new Date(),
|
|
184
|
+
});
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
### reset()
|
|
188
|
+
|
|
189
|
+
Clear user identity (on logout):
|
|
190
|
+
|
|
191
|
+
```typescript
|
|
192
|
+
analytics.reset();
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
---
|
|
196
|
+
|
|
197
|
+
## React Integration
|
|
198
|
+
|
|
199
|
+
### AnalyticsProvider
|
|
200
|
+
|
|
201
|
+
Wrap your app with the provider:
|
|
202
|
+
|
|
203
|
+
```tsx
|
|
204
|
+
import { AnalyticsProvider } from '@flight-framework/analytics/react';
|
|
205
|
+
|
|
206
|
+
function App() {
|
|
207
|
+
return (
|
|
208
|
+
<AnalyticsProvider client={analytics}>
|
|
209
|
+
<Routes />
|
|
210
|
+
</AnalyticsProvider>
|
|
211
|
+
);
|
|
212
|
+
}
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
### useAnalytics Hook
|
|
216
|
+
|
|
217
|
+
```tsx
|
|
218
|
+
import { useAnalytics } from '@flight-framework/analytics/react';
|
|
219
|
+
|
|
220
|
+
function SignupButton() {
|
|
221
|
+
const { trackEvent } = useAnalytics();
|
|
222
|
+
|
|
223
|
+
return (
|
|
224
|
+
<button onClick={() => trackEvent('signup_click', { location: 'header' })}>
|
|
225
|
+
Sign Up
|
|
226
|
+
</button>
|
|
227
|
+
);
|
|
228
|
+
}
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
### usePageview Hook
|
|
232
|
+
|
|
233
|
+
Automatic page view tracking:
|
|
234
|
+
|
|
235
|
+
```tsx
|
|
236
|
+
import { usePageview } from '@flight-framework/analytics/react';
|
|
237
|
+
|
|
238
|
+
function Page() {
|
|
239
|
+
usePageview(); // Tracks on mount and route changes
|
|
240
|
+
|
|
241
|
+
return <div>...</div>;
|
|
242
|
+
}
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
### TrackEvent Component
|
|
246
|
+
|
|
247
|
+
Declarative event tracking:
|
|
248
|
+
|
|
249
|
+
```tsx
|
|
250
|
+
import { TrackEvent } from '@flight-framework/analytics/react';
|
|
251
|
+
|
|
252
|
+
function PricingPage() {
|
|
253
|
+
return (
|
|
254
|
+
<>
|
|
255
|
+
<TrackEvent name="pricing_view" properties={{ variant: 'A' }} />
|
|
256
|
+
<h1>Pricing</h1>
|
|
257
|
+
</>
|
|
258
|
+
);
|
|
259
|
+
}
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
---
|
|
263
|
+
|
|
264
|
+
## Server-Side Tracking
|
|
265
|
+
|
|
266
|
+
Track events from your server or API routes:
|
|
267
|
+
|
|
268
|
+
```typescript
|
|
269
|
+
import { createServerAnalytics } from '@flight-framework/analytics/server';
|
|
270
|
+
|
|
271
|
+
const serverAnalytics = createServerAnalytics(posthog({
|
|
272
|
+
apiKey: process.env.POSTHOG_API_KEY,
|
|
273
|
+
}));
|
|
274
|
+
|
|
275
|
+
// In your API handler
|
|
276
|
+
export async function POST(request: Request) {
|
|
277
|
+
const { userId, action } = await request.json();
|
|
278
|
+
|
|
279
|
+
await serverAnalytics.trackEvent('api_action', {
|
|
280
|
+
action,
|
|
281
|
+
userId,
|
|
282
|
+
ip: request.headers.get('x-forwarded-for'),
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
return Response.json({ success: true });
|
|
286
|
+
}
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
---
|
|
290
|
+
|
|
291
|
+
## Privacy and Consent
|
|
292
|
+
|
|
293
|
+
### Consent Management
|
|
294
|
+
|
|
295
|
+
```typescript
|
|
296
|
+
const analytics = createAnalytics(adapter, {
|
|
297
|
+
consent: {
|
|
298
|
+
required: true, // Wait for consent before tracking
|
|
299
|
+
cookieName: 'consent',
|
|
300
|
+
defaultValue: false,
|
|
301
|
+
},
|
|
302
|
+
});
|
|
303
|
+
|
|
304
|
+
// User gives consent
|
|
305
|
+
analytics.setConsent(true);
|
|
306
|
+
|
|
307
|
+
// Check consent status
|
|
308
|
+
if (analytics.hasConsent()) {
|
|
309
|
+
// Track
|
|
310
|
+
}
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
### React Consent Hook
|
|
314
|
+
|
|
315
|
+
```tsx
|
|
316
|
+
import { useConsent } from '@flight-framework/analytics/react';
|
|
317
|
+
|
|
318
|
+
function CookieBanner() {
|
|
319
|
+
const { hasConsent, setConsent } = useConsent();
|
|
320
|
+
|
|
321
|
+
if (hasConsent) return null;
|
|
322
|
+
|
|
323
|
+
return (
|
|
324
|
+
<div className="cookie-banner">
|
|
325
|
+
<p>We use analytics to improve our site.</p>
|
|
326
|
+
<button onClick={() => setConsent(true)}>Accept</button>
|
|
327
|
+
<button onClick={() => setConsent(false)}>Decline</button>
|
|
328
|
+
</div>
|
|
329
|
+
);
|
|
330
|
+
}
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
---
|
|
334
|
+
|
|
335
|
+
## API Reference
|
|
336
|
+
|
|
337
|
+
### createAnalytics Options
|
|
338
|
+
|
|
339
|
+
| Option | Type | Default | Description |
|
|
340
|
+
|--------|------|---------|-------------|
|
|
341
|
+
| `debug` | `boolean` | `false` | Log events to console |
|
|
342
|
+
| `disabled` | `boolean` | `false` | Disable all tracking |
|
|
343
|
+
| `consent.required` | `boolean` | `false` | Require consent before tracking |
|
|
344
|
+
| `batch.enabled` | `boolean` | `true` | Batch events |
|
|
345
|
+
| `batch.size` | `number` | `10` | Max events per batch |
|
|
346
|
+
| `batch.timeout` | `number` | `5000` | Flush timeout in ms |
|
|
347
|
+
|
|
348
|
+
### Analytics Methods
|
|
349
|
+
|
|
350
|
+
| Method | Description |
|
|
351
|
+
|--------|-------------|
|
|
352
|
+
| `trackPageview(path, props?)` | Track page view |
|
|
353
|
+
| `trackEvent(name, props?)` | Track custom event |
|
|
354
|
+
| `identify(userId, traits?)` | Identify user |
|
|
355
|
+
| `reset()` | Clear user identity |
|
|
356
|
+
| `setConsent(value)` | Update consent |
|
|
357
|
+
| `hasConsent()` | Check consent status |
|
|
358
|
+
|
|
359
|
+
---
|
|
360
|
+
|
|
361
|
+
## License
|
|
362
|
+
|
|
363
|
+
MIT
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { AnalyticsAdapterFactory } from '../index.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Plausible Analytics Adapter
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
interface PlausibleConfig {
|
|
8
|
+
domain: string;
|
|
9
|
+
apiHost?: string;
|
|
10
|
+
}
|
|
11
|
+
declare const plausible: AnalyticsAdapterFactory<PlausibleConfig>;
|
|
12
|
+
|
|
13
|
+
export { type PlausibleConfig, plausible as default, plausible };
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
// src/adapters/plausible.ts
|
|
2
|
+
var plausible = (config) => {
|
|
3
|
+
const { domain, apiHost = "https://plausible.io" } = config;
|
|
4
|
+
async function send(eventName, props) {
|
|
5
|
+
try {
|
|
6
|
+
await fetch(`${apiHost}/api/event`, {
|
|
7
|
+
method: "POST",
|
|
8
|
+
headers: { "Content-Type": "application/json" },
|
|
9
|
+
body: JSON.stringify({
|
|
10
|
+
domain,
|
|
11
|
+
name: eventName,
|
|
12
|
+
url: typeof window !== "undefined" ? window.location.href : "",
|
|
13
|
+
referrer: typeof document !== "undefined" ? document.referrer : "",
|
|
14
|
+
screen_width: typeof window !== "undefined" ? window.innerWidth : void 0,
|
|
15
|
+
props
|
|
16
|
+
})
|
|
17
|
+
});
|
|
18
|
+
} catch {
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
const adapter = {
|
|
22
|
+
name: "plausible",
|
|
23
|
+
trackPageview(_data) {
|
|
24
|
+
send("pageview");
|
|
25
|
+
},
|
|
26
|
+
trackEvent(data) {
|
|
27
|
+
send(data.name, data.props);
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
return adapter;
|
|
31
|
+
};
|
|
32
|
+
var plausible_default = plausible;
|
|
33
|
+
export {
|
|
34
|
+
plausible_default as default,
|
|
35
|
+
plausible
|
|
36
|
+
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { AnalyticsAdapterFactory } from '../index.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* PostHog Analytics Adapter
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
interface PostHogConfig {
|
|
8
|
+
apiKey: string;
|
|
9
|
+
apiHost?: string;
|
|
10
|
+
}
|
|
11
|
+
declare const posthog: AnalyticsAdapterFactory<PostHogConfig>;
|
|
12
|
+
|
|
13
|
+
export { type PostHogConfig, posthog as default, posthog };
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
// src/adapters/posthog.ts
|
|
2
|
+
var posthog = (config) => {
|
|
3
|
+
const { apiKey, apiHost = "https://app.posthog.com" } = config;
|
|
4
|
+
let distinctId = `anon_${Math.random().toString(36).slice(2)}`;
|
|
5
|
+
async function capture(event, properties) {
|
|
6
|
+
try {
|
|
7
|
+
await fetch(`${apiHost}/capture/`, {
|
|
8
|
+
method: "POST",
|
|
9
|
+
headers: { "Content-Type": "application/json" },
|
|
10
|
+
body: JSON.stringify({
|
|
11
|
+
api_key: apiKey,
|
|
12
|
+
event,
|
|
13
|
+
properties: { distinct_id: distinctId, ...properties },
|
|
14
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
15
|
+
})
|
|
16
|
+
});
|
|
17
|
+
} catch {
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
const adapter = {
|
|
21
|
+
name: "posthog",
|
|
22
|
+
trackPageview(data) {
|
|
23
|
+
capture("$pageview", { $current_url: data.url, $referrer: data.referrer });
|
|
24
|
+
},
|
|
25
|
+
trackEvent(data) {
|
|
26
|
+
capture(data.name, data.props);
|
|
27
|
+
},
|
|
28
|
+
identify(userId, traits) {
|
|
29
|
+
distinctId = userId;
|
|
30
|
+
if (traits) {
|
|
31
|
+
capture("$identify", { $set: traits });
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
setUserId(userId) {
|
|
35
|
+
distinctId = userId ?? `anon_${Math.random().toString(36).slice(2)}`;
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
return adapter;
|
|
39
|
+
};
|
|
40
|
+
var posthog_default = posthog;
|
|
41
|
+
export {
|
|
42
|
+
posthog_default as default,
|
|
43
|
+
posthog
|
|
44
|
+
};
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @flightdev/analytics - Agnostic Analytics
|
|
3
|
+
*
|
|
4
|
+
* @example
|
|
5
|
+
* ```typescript
|
|
6
|
+
* import { createAnalytics } from '@flightdev/analytics';
|
|
7
|
+
* import { plausible } from '@flightdev/analytics/plausible';
|
|
8
|
+
*
|
|
9
|
+
* const analytics = createAnalytics(plausible({
|
|
10
|
+
* domain: 'example.com',
|
|
11
|
+
* }));
|
|
12
|
+
*
|
|
13
|
+
* analytics.trackPageview('/home');
|
|
14
|
+
* analytics.trackEvent('signup', { plan: 'pro' });
|
|
15
|
+
* ```
|
|
16
|
+
*/
|
|
17
|
+
interface PageviewData {
|
|
18
|
+
url: string;
|
|
19
|
+
referrer?: string;
|
|
20
|
+
deviceWidth?: number;
|
|
21
|
+
}
|
|
22
|
+
interface EventData {
|
|
23
|
+
name: string;
|
|
24
|
+
props?: Record<string, string | number | boolean>;
|
|
25
|
+
revenue?: {
|
|
26
|
+
amount: number;
|
|
27
|
+
currency: string;
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
interface AnalyticsAdapter {
|
|
31
|
+
readonly name: string;
|
|
32
|
+
trackPageview(data: PageviewData): void;
|
|
33
|
+
trackEvent(data: EventData): void;
|
|
34
|
+
identify?(userId: string, traits?: Record<string, unknown>): void;
|
|
35
|
+
setUserId?(userId: string | null): void;
|
|
36
|
+
}
|
|
37
|
+
type AnalyticsAdapterFactory<TConfig = unknown> = (config: TConfig) => AnalyticsAdapter;
|
|
38
|
+
interface AnalyticsService {
|
|
39
|
+
readonly adapter: AnalyticsAdapter;
|
|
40
|
+
trackPageview(url: string, options?: Omit<PageviewData, 'url'>): void;
|
|
41
|
+
trackEvent(name: string, props?: Record<string, string | number | boolean>): void;
|
|
42
|
+
identify(userId: string, traits?: Record<string, unknown>): void;
|
|
43
|
+
reset(): void;
|
|
44
|
+
}
|
|
45
|
+
declare function createAnalytics(adapter: AnalyticsAdapter): AnalyticsService;
|
|
46
|
+
declare function getDeviceType(width: number): 'mobile' | 'tablet' | 'desktop';
|
|
47
|
+
declare function parseUTMParams(url: string): Record<string, string>;
|
|
48
|
+
|
|
49
|
+
export { type AnalyticsAdapter, type AnalyticsAdapterFactory, type AnalyticsService, type EventData, type PageviewData, createAnalytics, getDeviceType, parseUTMParams };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
// src/index.ts
|
|
2
|
+
function createAnalytics(adapter) {
|
|
3
|
+
return {
|
|
4
|
+
adapter,
|
|
5
|
+
trackPageview(url, options) {
|
|
6
|
+
adapter.trackPageview({ url, ...options });
|
|
7
|
+
},
|
|
8
|
+
trackEvent(name, props) {
|
|
9
|
+
adapter.trackEvent({ name, props });
|
|
10
|
+
},
|
|
11
|
+
identify(userId, traits) {
|
|
12
|
+
adapter.identify?.(userId, traits);
|
|
13
|
+
},
|
|
14
|
+
reset() {
|
|
15
|
+
adapter.setUserId?.(null);
|
|
16
|
+
}
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
function getDeviceType(width) {
|
|
20
|
+
if (width < 768) return "mobile";
|
|
21
|
+
if (width < 1024) return "tablet";
|
|
22
|
+
return "desktop";
|
|
23
|
+
}
|
|
24
|
+
function parseUTMParams(url) {
|
|
25
|
+
const params = new URL(url).searchParams;
|
|
26
|
+
const utm = {};
|
|
27
|
+
for (const key of ["utm_source", "utm_medium", "utm_campaign", "utm_term", "utm_content"]) {
|
|
28
|
+
const value = params.get(key);
|
|
29
|
+
if (value) utm[key] = value;
|
|
30
|
+
}
|
|
31
|
+
return utm;
|
|
32
|
+
}
|
|
33
|
+
export {
|
|
34
|
+
createAnalytics,
|
|
35
|
+
getDeviceType,
|
|
36
|
+
parseUTMParams
|
|
37
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@flightdev/analytics",
|
|
3
|
+
"version": "0.0.2",
|
|
4
|
+
"description": "Agnostic analytics for Flight Framework. Choose your backend: Plausible, Umami, PostHog, or custom.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"exports": {
|
|
7
|
+
".": {
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"import": "./dist/index.js"
|
|
10
|
+
},
|
|
11
|
+
"./plausible": {
|
|
12
|
+
"types": "./dist/adapters/plausible.d.ts",
|
|
13
|
+
"import": "./dist/adapters/plausible.js"
|
|
14
|
+
},
|
|
15
|
+
"./posthog": {
|
|
16
|
+
"types": "./dist/adapters/posthog.d.ts",
|
|
17
|
+
"import": "./dist/adapters/posthog.js"
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
"files": [
|
|
21
|
+
"dist"
|
|
22
|
+
],
|
|
23
|
+
"devDependencies": {
|
|
24
|
+
"tsup": "^8.0.0",
|
|
25
|
+
"typescript": "^5.7.0",
|
|
26
|
+
"vitest": "^2.0.0"
|
|
27
|
+
},
|
|
28
|
+
"license": "MIT",
|
|
29
|
+
"scripts": {
|
|
30
|
+
"build": "tsup",
|
|
31
|
+
"test": "vitest run",
|
|
32
|
+
"typecheck": "tsc --noEmit"
|
|
33
|
+
}
|
|
34
|
+
}
|