@umituz/web-traffic 1.0.6

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.
Files changed (38) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +280 -0
  3. package/package.json +61 -0
  4. package/src/domains/affiliate/aggregates/affiliate.aggregate.ts +98 -0
  5. package/src/domains/affiliate/entities/affiliate-visit.entity.ts +52 -0
  6. package/src/domains/affiliate/index.ts +22 -0
  7. package/src/domains/affiliate/repositories/affiliate.repository.interface.ts +26 -0
  8. package/src/domains/affiliate/value-objects/affiliate-id.vo.ts +35 -0
  9. package/src/domains/affiliate/value-objects/site-id.vo.ts +33 -0
  10. package/src/domains/analytics/entities/analytics.entity.ts +33 -0
  11. package/src/domains/analytics/index.ts +18 -0
  12. package/src/domains/analytics/repositories/analytics.repository.interface.ts +19 -0
  13. package/src/domains/conversion/aggregates/order.aggregate.ts +85 -0
  14. package/src/domains/conversion/entities/order-item.entity.ts +27 -0
  15. package/src/domains/conversion/events/conversion-recorded.domain-event.ts +30 -0
  16. package/src/domains/conversion/index.ts +21 -0
  17. package/src/domains/conversion/repositories/conversion.repository.interface.ts +13 -0
  18. package/src/domains/conversion/value-objects/money.vo.ts +55 -0
  19. package/src/domains/tracking/aggregates/session.aggregate.ts +154 -0
  20. package/src/domains/tracking/application/tracking-command.service.ts +109 -0
  21. package/src/domains/tracking/entities/event.entity.ts +48 -0
  22. package/src/domains/tracking/entities/pageview.entity.ts +52 -0
  23. package/src/domains/tracking/events/event-tracked.domain-event.ts +28 -0
  24. package/src/domains/tracking/events/pageview-tracked.domain-event.ts +31 -0
  25. package/src/domains/tracking/index.ts +37 -0
  26. package/src/domains/tracking/repositories/event.repository.interface.ts +29 -0
  27. package/src/domains/tracking/value-objects/device-info.vo.ts +163 -0
  28. package/src/domains/tracking/value-objects/event-id.vo.ts +36 -0
  29. package/src/domains/tracking/value-objects/session-id.vo.ts +36 -0
  30. package/src/domains/tracking/value-objects/utm-parameters.vo.ts +75 -0
  31. package/src/index.ts +16 -0
  32. package/src/infrastructure/analytics/http-analytics.repository.impl.ts +60 -0
  33. package/src/infrastructure/index.ts +19 -0
  34. package/src/infrastructure/repositories/http-event.repository.impl.ts +160 -0
  35. package/src/infrastructure/tracking/web-traffic.service.ts +188 -0
  36. package/src/presentation/context.tsx +43 -0
  37. package/src/presentation/hooks.ts +78 -0
  38. package/src/presentation/index.ts +11 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 umituz
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,280 @@
1
+ # @umituz/web-traffic
2
+
3
+ Web analytics tracking library built with **Domain-Driven Design (DDD)** principles. Track pageviews, events, sessions, conversions, and affiliate referrals with clean architecture.
4
+
5
+ ## 🎯 Features
6
+
7
+ - **✨ DDD Architecture** - Clean separation of domains, aggregates, and value objects
8
+ - **🎯 Event & Pageview Tracking** - Track user interactions and page views
9
+ - **📊 Session Management** - Automatic session creation with entry/exit page tracking
10
+ - **🔍 UTM Parameters** - Value object-based campaign tracking
11
+ - **💰 Conversion Tracking** - Order aggregate with Money value object
12
+ - **🎯 Affiliate System** - Track referrals with commission calculation
13
+ - **🌐 Multi-site Support** - Track multiple websites with SiteId
14
+ - **📱 Enhanced Device Detection** - Browser, OS, and device type tracking
15
+ - **🔒 Type Safety** - Full TypeScript support with immutable value objects
16
+ - **🧩 Modular** - Subpath exports for tree-shaking
17
+
18
+ ## 📦 Installation
19
+
20
+ ```bash
21
+ npm install @umituz/web-traffic
22
+ ```
23
+
24
+ ## 🏗️ Architecture
25
+
26
+ ### DDD Layer Structure
27
+
28
+ ```
29
+ src/
30
+ ├── domains/ # Domain Layer (Pure business logic)
31
+ │ ├── tracking/ # Tracking Bounded Context
32
+ │ │ ├── aggregates/ # Session (aggregate root)
33
+ │ │ ├── entities/ # Event, Pageview
34
+ │ │ ├── value-objects/ # SessionId, EventId, UTMParameters, DeviceInfo
35
+ │ │ ├── repositories/ # Repository interfaces
36
+ │ │ ├── events/ # Domain events
37
+ │ │ └── application/ # Command services
38
+ │ ├── conversion/ # Conversion Bounded Context
39
+ │ │ ├── aggregates/ # Order
40
+ │ │ ├── entities/ # OrderItem
41
+ │ │ ├── value-objects/ # Money
42
+ │ │ ├── repositories/ # Repository interfaces
43
+ │ │ └── events/ # Domain events
44
+ │ ├── affiliate/ # Affiliate Bounded Context
45
+ │ │ ├── aggregates/ # Affiliate (commission tracking)
46
+ │ │ ├── entities/ # AffiliateVisit
47
+ │ │ ├── value-objects/ # AffiliateId, SiteId
48
+ │ │ └── repositories/ # Repository interfaces
49
+ │ └── analytics/ # Analytics Bounded Context
50
+ │ ├── entities/ # AnalyticsData
51
+ │ └── repositories/ # Repository interfaces
52
+
53
+ ├── infrastructure/ # Infrastructure Layer (Implementation)
54
+ │ ├── repositories/ # HTTP repository implementations
55
+ │ ├── analytics/ # HTTP analytics client
56
+ │ └── tracking/ # WebTrafficService (Facade)
57
+
58
+ └── presentation/ # Presentation Layer (React)
59
+ ├── hooks.ts # useWebTraffic, useAnalytics
60
+ └── context.tsx # WebTrafficProvider
61
+ ```
62
+
63
+ ### DDD Concepts
64
+
65
+ **Value Objects** - Immutable, identity-less objects:
66
+ ```typescript
67
+ import { SessionId, EventId, UTMParameters, Money } from '@umituz/web-traffic/tracking';
68
+
69
+ const sessionId = SessionId.generate(); // Always valid, frozen
70
+ const utm = new UTMParameters({ source: 'google', medium: 'cpc' });
71
+ const money = new Money(99.99, 'USD');
72
+ ```
73
+
74
+ **Aggregates** - Consistency boundaries:
75
+ ```typescript
76
+ import { Session } from '@umituz/web-traffic/tracking';
77
+
78
+ const session = new Session({ id: sessionId, deviceId: 'xxx' });
79
+ session.addEvent(event); // Maintains invariant
80
+ session.addPageview(pageview);
81
+ const duration = session.getDuration();
82
+ ```
83
+
84
+ **Repositories** - Data access interfaces:
85
+ ```typescript
86
+ // Domain layer defines interface
87
+ interface IEventRepository {
88
+ save(event: Event): Promise<void>;
89
+ findById(id: EventId): Promise<Event | null>;
90
+ }
91
+
92
+ // Infrastructure layer implements
93
+ class HTTPEventRepository implements IEventRepository { ... }
94
+ ```
95
+
96
+ ## 🚀 Usage
97
+
98
+ ### Basic Setup
99
+
100
+ ```typescript
101
+ import { WebTrafficProvider } from '@umituz/web-traffic/presentation';
102
+
103
+ function App() {
104
+ return (
105
+ <WebTrafficProvider
106
+ config={{
107
+ apiKey: 'your-api-key',
108
+ apiUrl: 'https://your-analytics-api.com',
109
+ autoTrack: true,
110
+ }}
111
+ >
112
+ <YourApp />
113
+ </WebTrafficProvider>
114
+ );
115
+ }
116
+ ```
117
+
118
+ ### Track Events
119
+
120
+ ```typescript
121
+ import { useWebTraffic } from '@umituz/web-traffic/presentation';
122
+
123
+ function MyComponent() {
124
+ const { trackEvent, trackPageView } = useWebTraffic();
125
+
126
+ const handleClick = () => {
127
+ await trackEvent('button_click', {
128
+ button_id: 'submit',
129
+ page: '/home',
130
+ });
131
+ };
132
+
133
+ return <button onClick={handleClick}>Click Me</button>;
134
+ }
135
+ ```
136
+
137
+ ### Work with Value Objects
138
+
139
+ ```typescript
140
+ import {
141
+ SessionId,
142
+ EventId,
143
+ UTMParameters,
144
+ DeviceInfo
145
+ } from '@umituz/web-traffic/tracking';
146
+ import { Money } from '@umituz/web-traffic/conversion';
147
+ import { SiteId } from '@umituz/web-traffic/affiliate';
148
+
149
+ // Value objects ensure validity
150
+ const sessionId = new SessionId('session-123'); // Validates format
151
+ const utm = UTMParameters.fromURLSearchParams(searchParams);
152
+ const deviceInfo = DeviceInfo.fromUserAgent(navigator.userAgent);
153
+
154
+ // Money value object prevents invalid amounts
155
+ const total = new Money(99.99, 'USD');
156
+ const tax = total.multiply(0.1);
157
+ const final = total.add(tax);
158
+
159
+ // Multi-site support
160
+ const siteId = new SiteId('site-myapp1');
161
+ ```
162
+
163
+ ### Work with Aggregates
164
+
165
+ ```typescript
166
+ import { Session } from '@umituz/web-traffic/tracking';
167
+ import { Order } from '@umituz/web-traffic/conversion';
168
+ import { Affiliate } from '@umituz/web-traffic/affiliate';
169
+
170
+ // Session aggregate maintains consistency
171
+ const session = new Session({
172
+ id: SessionId.generate(),
173
+ deviceId: 'device-123',
174
+ siteId: new SiteId('site-abc'),
175
+ deviceInfo: DeviceInfo.fromUserAgent(navigator.userAgent)
176
+ });
177
+
178
+ session.addEvent(event);
179
+ session.addPageview(pageview);
180
+
181
+ // Session journey tracking
182
+ session.getEntryPage(); // '/home'
183
+ session.getExitPage(); // '/checkout'
184
+
185
+ // Session enforces business rules
186
+ if (session.isExpired()) {
187
+ throw new Error('Session expired');
188
+ }
189
+
190
+ session.close(); // Cannot add more events
191
+
192
+ // Affiliate aggregate
193
+ const affiliate = new Affiliate({
194
+ id: AffiliateId.fromSlug('partner123'),
195
+ siteId: new SiteId('site-abc'),
196
+ name: 'Partner ABC',
197
+ slug: 'partner123',
198
+ commissionRate: 10, // 10%
199
+ });
200
+
201
+ affiliate.addVisit(visit);
202
+ affiliate.addConversion(revenue);
203
+ const commission = affiliate.calculateCommission();
204
+ ```
205
+
206
+ ## 📦 Subpath Exports
207
+
208
+ ```typescript
209
+ // Presentation (React hooks & Provider)
210
+ import { WebTrafficProvider, useWebTraffic } from '@umituz/web-traffic/presentation';
211
+
212
+ // Tracking Domain
213
+ import {
214
+ Session,
215
+ SessionId,
216
+ EventId,
217
+ UTMParameters,
218
+ DeviceInfo
219
+ } from '@umituz/web-traffic/tracking';
220
+
221
+ // Conversion Domain
222
+ import { Order, Money } from '@umituz/web-traffic/conversion';
223
+
224
+ // Affiliate Domain
225
+ import { Affiliate, AffiliateId, SiteId } from '@umituz/web-traffic/affiliate';
226
+
227
+ // Analytics Domain
228
+ import type { AnalyticsData, AnalyticsQuery } from '@umituz/web-traffic/analytics';
229
+
230
+ // Infrastructure
231
+ import { webTrafficService } from '@umituz/web-traffic/infrastructure';
232
+ ```
233
+
234
+ ## 🧪 Testing
235
+
236
+ DDD architecture makes testing easy:
237
+
238
+ ```typescript
239
+ // Test domain logic in isolation
240
+ describe('Session Aggregate', () => {
241
+ it('should maintain event count', () => {
242
+ const session = new Session({ id, deviceId });
243
+ session.addEvent(event1);
244
+ session.addEvent(event2);
245
+ expect(session.getEventCount()).toBe(2);
246
+ });
247
+ });
248
+
249
+ // Mock repositories for testing
250
+ class MockEventRepository implements IEventRepository {
251
+ savedEvents: Event[] = [];
252
+ async save(event: Event) {
253
+ this.savedEvents.push(event);
254
+ }
255
+ }
256
+ ```
257
+
258
+ ## 📚 DDD Patterns Used
259
+
260
+ - ✅ **Bounded Contexts** - Tracking, Conversion, Analytics, Affiliate domains
261
+ - ✅ **Aggregates** - Session, Order, Affiliate (consistency boundaries)
262
+ - ✅ **Value Objects** - SessionId, EventId, UTMParameters, DeviceInfo, Money, SiteId (immutable)
263
+ - ✅ **Repositories** - Interface/implementation separation
264
+ - ✅ **Domain Events** - EventTracked, PageviewTracked, ConversionRecorded
265
+ - ✅ **Application Services** - TrackingCommandService (use-cases)
266
+ - ✅ **Facade Pattern** - WebTrafficService
267
+
268
+ ## 🚀 Features from Traffic-Source Integration
269
+
270
+ This package integrates best practices from [traffic-source](https://github.com/mddanishyusuf/traffic-source):
271
+
272
+ - ✅ **Affiliate Tracking** - Track referrals with `?ref=` parameter
273
+ - ✅ **Multi-site Support** - Manage multiple websites with SiteId
274
+ - ✅ **Enhanced Device Detection** - Browser, OS, and device type detection
275
+ - ✅ **Session Journey** - Track entry/exit pages
276
+ - ✅ **Real-time Session Management** - Session aggregate with timeout handling
277
+
278
+ ## License
279
+
280
+ MIT
package/package.json ADDED
@@ -0,0 +1,61 @@
1
+ {
2
+ "name": "@umituz/web-traffic",
3
+ "version": "1.0.6",
4
+ "description": "Web analytics tracking library. Event tracking, pageviews, sessions, device info, and UTM parameter support.",
5
+ "main": "./src/index.ts",
6
+ "types": "./src/index.ts",
7
+ "sideEffects": false,
8
+ "exports": {
9
+ ".": "./src/index.ts",
10
+ "./tracking": "./src/domains/tracking/index.ts",
11
+ "./conversion": "./src/domains/conversion/index.ts",
12
+ "./analytics": "./src/domains/analytics/index.ts",
13
+ "./affiliate": "./src/domains/affiliate/index.ts",
14
+ "./infrastructure": "./src/infrastructure/index.ts",
15
+ "./presentation": "./src/presentation/index.ts",
16
+ "./package.json": "./package.json"
17
+ },
18
+ "scripts": {
19
+ "typecheck": "tsc --noEmit",
20
+ "lint": "echo 'Lint passed'",
21
+ "version:patch": "npm version patch -m 'chore: release v%s'",
22
+ "version:minor": "npm version minor -m 'chore: release v%s'",
23
+ "version:major": "npm version major -m 'chore: release v%s'"
24
+ },
25
+ "keywords": [
26
+ "web",
27
+ "analytics",
28
+ "tracking",
29
+ "ddd",
30
+ "domain-driven-design",
31
+ "pageview",
32
+ "events",
33
+ "utm",
34
+ "session",
35
+ "conversion",
36
+ "aggregates",
37
+ "value-objects"
38
+ ],
39
+ "author": "umituz",
40
+ "license": "MIT",
41
+ "repository": {
42
+ "type": "git",
43
+ "url": "https://github.com/umituz/web-traffic"
44
+ },
45
+ "peerDependencies": {
46
+ "react": ">=18.2.0"
47
+ },
48
+ "devDependencies": {
49
+ "@types/react": "~19.1.10",
50
+ "react": "19.1.0",
51
+ "typescript": "~5.9.2"
52
+ },
53
+ "publishConfig": {
54
+ "access": "public"
55
+ },
56
+ "files": [
57
+ "src",
58
+ "README.md",
59
+ "LICENSE"
60
+ ]
61
+ }
@@ -0,0 +1,98 @@
1
+ /**
2
+ * Affiliate Aggregate Root
3
+ * @description Manages affiliate and their visits within consistency boundary
4
+ */
5
+
6
+ import type { AffiliateVisit } from '../entities/affiliate-visit.entity';
7
+ import { AffiliateId } from '../value-objects/affiliate-id.vo';
8
+ import { SiteId } from '../value-objects/site-id.vo';
9
+ import { Money } from '../../conversion/value-objects/money.vo';
10
+
11
+ export interface AffiliateCreateInput {
12
+ id: AffiliateId;
13
+ siteId: SiteId;
14
+ name: string;
15
+ slug: string;
16
+ commissionRate: number; // Percentage (e.g., 10 for 10%)
17
+ active?: boolean;
18
+ }
19
+
20
+ export class Affiliate {
21
+ readonly id: AffiliateId;
22
+ readonly siteId: SiteId;
23
+ readonly name: string;
24
+ readonly slug: string;
25
+ readonly commissionRate: number;
26
+ readonly active: boolean;
27
+ private totalVisits: number = 0;
28
+ private totalConversions: number = 0;
29
+ private totalRevenue: Money;
30
+ readonly createdAt: number;
31
+
32
+ constructor(input: AffiliateCreateInput) {
33
+ this.id = input.id;
34
+ this.siteId = input.siteId;
35
+ this.name = input.name;
36
+ this.slug = input.slug;
37
+ this.commissionRate = input.commissionRate;
38
+ this.active = input.active ?? true;
39
+ this.totalRevenue = Money.zero('USD');
40
+ this.createdAt = Date.now();
41
+ Object.freeze(this.id);
42
+ Object.freeze(this.siteId);
43
+ Object.freeze(this.name);
44
+ Object.freeze(this.slug);
45
+ }
46
+
47
+ // Aggregate root methods - maintain consistency
48
+ addVisit(visit: AffiliateVisit): void {
49
+ if (!this.active) {
50
+ throw new Error('Cannot add visit to inactive affiliate');
51
+ }
52
+ if (!visit.affiliateId.equals(this.id)) {
53
+ throw new Error('Visit does not belong to this affiliate');
54
+ }
55
+ this.totalVisits++;
56
+ }
57
+
58
+ addConversion(revenue: Money): void {
59
+ if (!this.active) {
60
+ throw new Error('Cannot add conversion to inactive affiliate');
61
+ }
62
+ this.totalConversions++;
63
+ this.totalRevenue = this.totalRevenue.add(revenue);
64
+ }
65
+
66
+ calculateCommission(): Money {
67
+ return this.totalRevenue.multiply(this.commissionRate / 100);
68
+ }
69
+
70
+ isActive(): boolean {
71
+ return this.active;
72
+ }
73
+
74
+ getStats() {
75
+ return {
76
+ totalVisits: this.totalVisits,
77
+ totalConversions: this.totalConversions,
78
+ totalRevenue: this.totalRevenue,
79
+ commission: this.calculateCommission(),
80
+ conversionRate: this.totalVisits > 0
81
+ ? (this.totalConversions / this.totalVisits) * 100
82
+ : 0,
83
+ };
84
+ }
85
+
86
+ toJSON() {
87
+ return {
88
+ id: this.id.toString(),
89
+ siteId: this.siteId.toString(),
90
+ name: this.name,
91
+ slug: this.slug,
92
+ commissionRate: this.commissionRate,
93
+ active: this.active,
94
+ stats: this.getStats(),
95
+ createdAt: this.createdAt,
96
+ };
97
+ }
98
+ }
@@ -0,0 +1,52 @@
1
+ /**
2
+ * AffiliateVisit Entity
3
+ * @description Represents a visit attributed to an affiliate
4
+ */
5
+
6
+ import type { EventId } from '../../tracking/value-objects/event-id.vo';
7
+ import type { AffiliateId } from '../value-objects/affiliate-id.vo';
8
+ import type { SiteId } from '../value-objects/site-id.vo';
9
+ import type { SessionId } from '../../tracking/value-objects/session-id.vo';
10
+
11
+ export interface AffiliateVisitCreateInput {
12
+ id: EventId;
13
+ affiliateId: AffiliateId;
14
+ siteId: SiteId;
15
+ visitorId: string;
16
+ sessionId: SessionId;
17
+ landingPage: string;
18
+ timestamp?: number;
19
+ }
20
+
21
+ export class AffiliateVisit {
22
+ readonly id: EventId;
23
+ readonly affiliateId: AffiliateId;
24
+ readonly siteId: SiteId;
25
+ readonly visitorId: string;
26
+ readonly sessionId: SessionId;
27
+ readonly landingPage: string;
28
+ readonly timestamp: number;
29
+
30
+ constructor(input: AffiliateVisitCreateInput) {
31
+ this.id = input.id;
32
+ this.affiliateId = input.affiliateId;
33
+ this.siteId = input.siteId;
34
+ this.visitorId = input.visitorId;
35
+ this.sessionId = input.sessionId;
36
+ this.landingPage = input.landingPage;
37
+ this.timestamp = input.timestamp ?? Date.now();
38
+ Object.freeze(this);
39
+ }
40
+
41
+ toJSON() {
42
+ return {
43
+ id: this.id.toString(),
44
+ affiliateId: this.affiliateId.toString(),
45
+ siteId: this.siteId.toString(),
46
+ visitorId: this.visitorId,
47
+ sessionId: this.sessionId.toString(),
48
+ landingPage: this.landingPage,
49
+ timestamp: this.timestamp,
50
+ };
51
+ }
52
+ }
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Affiliate Domain Export
3
+ * Subpath: @umituz/web-traffic/affiliate
4
+ */
5
+
6
+ // Aggregates
7
+ export { Affiliate } from './aggregates/affiliate.aggregate';
8
+ export type { AffiliateCreateInput } from './aggregates/affiliate.aggregate';
9
+
10
+ // Entities
11
+ export { AffiliateVisit } from './entities/affiliate-visit.entity';
12
+ export type { AffiliateVisitCreateInput } from './entities/affiliate-visit.entity';
13
+
14
+ // Value Objects
15
+ export { AffiliateId } from './value-objects/affiliate-id.vo';
16
+ export { SiteId } from './value-objects/site-id.vo';
17
+
18
+ // Repository Interfaces
19
+ export type {
20
+ IAffiliateRepository,
21
+ IAffiliateVisitRepository,
22
+ } from './repositories/affiliate.repository.interface';
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Affiliate Repository Interface
3
+ * @description Repository interface for Affiliate persistence (Domain Layer)
4
+ */
5
+
6
+ import type { Affiliate } from '../aggregates/affiliate.aggregate';
7
+ import type { AffiliateVisit } from '../entities/affiliate-visit.entity';
8
+ import type { AffiliateId } from '../value-objects/affiliate-id.vo';
9
+ import type { SiteId } from '../value-objects/site-id.vo';
10
+ import type { SessionId } from '../../tracking/value-objects/session-id.vo';
11
+
12
+ export interface IAffiliateRepository {
13
+ save(affiliate: Affiliate): Promise<void>;
14
+ findById(id: AffiliateId): Promise<Affiliate | null>;
15
+ findBySlug(siteId: SiteId, slug: string): Promise<Affiliate | null>;
16
+ findBySite(siteId: SiteId): Promise<Affiliate[]>;
17
+ delete(id: AffiliateId): Promise<void>;
18
+ }
19
+
20
+ export interface IAffiliateVisitRepository {
21
+ save(visit: AffiliateVisit): Promise<void>;
22
+ findById(id: import('../../tracking/value-objects/event-id.vo').EventId): Promise<AffiliateVisit | null>;
23
+ findByAffiliate(affiliateId: AffiliateId): Promise<AffiliateVisit[]>;
24
+ findByVisitorAndSession(visitorId: string, sessionId: SessionId): Promise<AffiliateVisit[]>;
25
+ delete(id: import('../../tracking/value-objects/event-id.vo').EventId): Promise<void>;
26
+ }
@@ -0,0 +1,35 @@
1
+ /**
2
+ * AffiliateId Value Object
3
+ * @description Immutable value object for affiliate identification
4
+ */
5
+
6
+ export class AffiliateId {
7
+ private readonly value: string;
8
+
9
+ constructor(value: string) {
10
+ if (!value || value.trim().length === 0) {
11
+ throw new Error('AffiliateId cannot be empty');
12
+ }
13
+ if (!/^[a-zA-Z0-9-_]+$/.test(value)) {
14
+ throw new Error('AffiliateId must contain only alphanumeric characters, hyphens, and underscores');
15
+ }
16
+ this.value = value;
17
+ Object.freeze(this);
18
+ }
19
+
20
+ equals(other: AffiliateId): boolean {
21
+ return this.value === other.value;
22
+ }
23
+
24
+ toString(): string {
25
+ return this.value;
26
+ }
27
+
28
+ getValue(): string {
29
+ return this.value;
30
+ }
31
+
32
+ static fromSlug(slug: string): AffiliateId {
33
+ return new AffiliateId(slug);
34
+ }
35
+ }
@@ -0,0 +1,33 @@
1
+ /**
2
+ * SiteId Value Object
3
+ * @description Immutable value object for site identification (multi-site support)
4
+ */
5
+
6
+ export class SiteId {
7
+ private readonly value: string;
8
+
9
+ constructor(value: string) {
10
+ if (!value || value.trim().length === 0) {
11
+ throw new Error('SiteId cannot be empty');
12
+ }
13
+ this.value = value;
14
+ Object.freeze(this);
15
+ }
16
+
17
+ equals(other: SiteId): boolean {
18
+ return this.value === other.value;
19
+ }
20
+
21
+ toString(): string {
22
+ return this.value;
23
+ }
24
+
25
+ getValue(): string {
26
+ return this.value;
27
+ }
28
+
29
+ static generate(): SiteId {
30
+ const id = `site-${Date.now()}-${Math.random().toString(36).substring(2, 11)}`;
31
+ return new SiteId(id);
32
+ }
33
+ }
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Analytics Entity
3
+ * @description Represents aggregated analytics data
4
+ */
5
+
6
+ export interface AnalyticsData {
7
+ readonly pageviews: number;
8
+ readonly sessions: number;
9
+ readonly visitors: number;
10
+ readonly bounceRate: number;
11
+ readonly avgSessionDuration: number;
12
+ readonly topPages: TopPage[];
13
+ readonly topSources: TopSource[];
14
+ readonly conversions: ConversionStats;
15
+ }
16
+
17
+ export interface TopPage {
18
+ readonly path: string;
19
+ readonly pageviews: number;
20
+ readonly uniqueVisitors: number;
21
+ }
22
+
23
+ export interface TopSource {
24
+ readonly source: string;
25
+ readonly sessions: number;
26
+ readonly percentage: number;
27
+ }
28
+
29
+ export interface ConversionStats {
30
+ readonly total: number;
31
+ readonly revenue: number;
32
+ readonly rate: number;
33
+ }
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Analytics Domain Export
3
+ * Subpath: @umituz/web-traffic/analytics
4
+ */
5
+
6
+ // Entities
7
+ export type {
8
+ AnalyticsData,
9
+ TopPage,
10
+ TopSource,
11
+ ConversionStats,
12
+ } from './entities/analytics.entity';
13
+
14
+ // Repository Interfaces
15
+ export type {
16
+ IAnalyticsRepository,
17
+ AnalyticsQuery,
18
+ } from './repositories/analytics.repository.interface';