@trakoo/emitkit 0.0.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 +41 -0
- package/dist/server.d.ts +104 -0
- package/dist/server.js +314 -0
- package/package.json +55 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 trakoo
|
|
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,41 @@
|
|
|
1
|
+
# @trakoo/emitkit
|
|
2
|
+
|
|
3
|
+
EmitKit server provider for [trakoo](https://www.npmjs.com/package/trakoo), the typed, provider-agnostic analytics library.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pnpm add trakoo @trakoo/emitkit @emitkit/js@next
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Usage
|
|
12
|
+
|
|
13
|
+
`appEvents` is your event registry, created with `defineEvents()` from `trakoo`.
|
|
14
|
+
|
|
15
|
+
```typescript title="lib/server-analytics.ts"
|
|
16
|
+
import { createServerAnalytics } from 'trakoo/server';
|
|
17
|
+
import { EmitKitServerProvider } from '@trakoo/emitkit/server';
|
|
18
|
+
import { appEvents } from './events';
|
|
19
|
+
|
|
20
|
+
export const serverAnalytics = createServerAnalytics({
|
|
21
|
+
events: appEvents,
|
|
22
|
+
providers: [
|
|
23
|
+
new EmitKitServerProvider({
|
|
24
|
+
apiKey: process.env.EMITKIT_API_KEY!,
|
|
25
|
+
channelName: 'general',
|
|
26
|
+
categoryChannelMap: {
|
|
27
|
+
user: 'user-activity',
|
|
28
|
+
conversion: 'revenue'
|
|
29
|
+
}
|
|
30
|
+
})
|
|
31
|
+
]
|
|
32
|
+
});
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
EmitKit is server-only. To send browser events to it, forward them through trakoo's [Proxy provider](https://trakoo.co/docs/providers/proxy).
|
|
36
|
+
|
|
37
|
+
The provider also sends page views, as silent events. EmitKit allows 100 requests per minute per API key by default, so if you only want explicit events in your feeds, register it as `{ provider: new EmitKitServerProvider({ ... }), exclude: ['pageView'] }`.
|
|
38
|
+
|
|
39
|
+
## Documentation
|
|
40
|
+
|
|
41
|
+
https://trakoo.co/docs/providers/emitkit
|
package/dist/server.d.ts
ADDED
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { BaseAnalyticsProvider, type BaseEvent, type EventContext } from "trakoo";
|
|
2
|
+
/**
|
|
3
|
+
* Configuration for EmitKit server provider
|
|
4
|
+
*/
|
|
5
|
+
export interface EmitKitServerConfig {
|
|
6
|
+
/**
|
|
7
|
+
* Your EmitKit API key (starts with emitkit_)
|
|
8
|
+
*/
|
|
9
|
+
apiKey: string;
|
|
10
|
+
/**
|
|
11
|
+
* Request timeout in milliseconds. A request that fails or times out is not
|
|
12
|
+
* retried.
|
|
13
|
+
* @default 5000
|
|
14
|
+
*/
|
|
15
|
+
timeout?: number;
|
|
16
|
+
/**
|
|
17
|
+
* Default channel name for events
|
|
18
|
+
* @default 'general'
|
|
19
|
+
*/
|
|
20
|
+
channelName?: string;
|
|
21
|
+
/**
|
|
22
|
+
* Map event categories to specific EmitKit channels.
|
|
23
|
+
* Allows automatic routing of events to appropriate channels based on category.
|
|
24
|
+
*
|
|
25
|
+
* @example
|
|
26
|
+
* ```typescript
|
|
27
|
+
* {
|
|
28
|
+
* 'user': 'user-activity',
|
|
29
|
+
* 'engagement': 'product-usage',
|
|
30
|
+
* 'error': 'alerts',
|
|
31
|
+
* 'conversion': 'revenue'
|
|
32
|
+
* }
|
|
33
|
+
* ```
|
|
34
|
+
*
|
|
35
|
+
* Channel resolution priority:
|
|
36
|
+
* 1. Event property `__emitkit_channel` (highest priority)
|
|
37
|
+
* 2. Category mapping via `categoryChannelMap`
|
|
38
|
+
* 3. Default `channelName` (fallback, default: 'general')
|
|
39
|
+
*/
|
|
40
|
+
categoryChannelMap?: Record<string, string>;
|
|
41
|
+
/**
|
|
42
|
+
* Send notification for events
|
|
43
|
+
* @default true
|
|
44
|
+
*/
|
|
45
|
+
notify?: boolean;
|
|
46
|
+
/**
|
|
47
|
+
* Display style for events
|
|
48
|
+
* @default 'notification'
|
|
49
|
+
*/
|
|
50
|
+
displayAs?: "message" | "notification";
|
|
51
|
+
/**
|
|
52
|
+
* Enable debug logging
|
|
53
|
+
*/
|
|
54
|
+
debug?: boolean;
|
|
55
|
+
/**
|
|
56
|
+
* Enable/disable the provider
|
|
57
|
+
*/
|
|
58
|
+
enabled?: boolean;
|
|
59
|
+
}
|
|
60
|
+
export declare class EmitKitServerProvider extends BaseAnalyticsProvider {
|
|
61
|
+
name: string;
|
|
62
|
+
private client?;
|
|
63
|
+
private initialized;
|
|
64
|
+
private config;
|
|
65
|
+
constructor(config: EmitKitServerConfig);
|
|
66
|
+
initialize(): Promise<void>;
|
|
67
|
+
identify(userId: string, traits?: Record<string, unknown>): Promise<void>;
|
|
68
|
+
track(event: BaseEvent, context?: EventContext): Promise<void>;
|
|
69
|
+
pageView(properties?: Record<string, unknown>, context?: EventContext): Promise<void>;
|
|
70
|
+
reset(): Promise<void>;
|
|
71
|
+
shutdown(): Promise<void>;
|
|
72
|
+
/**
|
|
73
|
+
* Request context attached to event metadata. EmitKit shows metadata in
|
|
74
|
+
* team feeds and notifications, so the visitor IP address (which proxy
|
|
75
|
+
* ingestion adds to `device`) is never forwarded.
|
|
76
|
+
*/
|
|
77
|
+
private getContextMetadata;
|
|
78
|
+
/**
|
|
79
|
+
* Describe an SDK failure for logs. EmitKit errors add their error code,
|
|
80
|
+
* HTTP status, and request id, which tell auth, validation, and rate-limit
|
|
81
|
+
* failures apart; the message and response body are never logged.
|
|
82
|
+
*/
|
|
83
|
+
private describeError;
|
|
84
|
+
/**
|
|
85
|
+
* Format event action into a human-readable title
|
|
86
|
+
* Converts: "user_signed_up" -> "User Signed Up"
|
|
87
|
+
*/
|
|
88
|
+
private formatEventTitle;
|
|
89
|
+
/**
|
|
90
|
+
* Generate a description for the event
|
|
91
|
+
*/
|
|
92
|
+
private getEventDescription;
|
|
93
|
+
/**
|
|
94
|
+
* Get an appropriate icon for the event category
|
|
95
|
+
*/
|
|
96
|
+
private getEventIcon;
|
|
97
|
+
/**
|
|
98
|
+
* Resolve the channel name for an event based on priority:
|
|
99
|
+
* 1. Event property __emitkit_channel (highest priority)
|
|
100
|
+
* 2. Category mapping via categoryChannelMap
|
|
101
|
+
* 3. Default channelName (fallback, default: 'general')
|
|
102
|
+
*/
|
|
103
|
+
private resolveChannelName;
|
|
104
|
+
}
|
package/dist/server.js
ADDED
|
@@ -0,0 +1,314 @@
|
|
|
1
|
+
import { BaseAnalyticsProvider, } from "trakoo";
|
|
2
|
+
const DEFAULT_TIMEOUT = 5000;
|
|
3
|
+
/** Identifies trakoo as the sender of each event in EmitKit. */
|
|
4
|
+
const EVENT_SOURCE = "trakoo";
|
|
5
|
+
export class EmitKitServerProvider extends BaseAnalyticsProvider {
|
|
6
|
+
name = "EmitKit-Server";
|
|
7
|
+
client;
|
|
8
|
+
initialized = false;
|
|
9
|
+
config;
|
|
10
|
+
constructor(config) {
|
|
11
|
+
super({ debug: config.debug, enabled: config.enabled });
|
|
12
|
+
this.config = config;
|
|
13
|
+
}
|
|
14
|
+
async initialize() {
|
|
15
|
+
if (!this.isEnabled())
|
|
16
|
+
return;
|
|
17
|
+
if (this.initialized)
|
|
18
|
+
return;
|
|
19
|
+
// Validate config has required fields
|
|
20
|
+
if (!this.config.apiKey || typeof this.config.apiKey !== "string") {
|
|
21
|
+
throw new Error("EmitKit requires an apiKey");
|
|
22
|
+
}
|
|
23
|
+
if (!this.config.apiKey.startsWith("emitkit_")) {
|
|
24
|
+
console.warn("[EmitKit-Server] API key should start with 'emitkit_'. Double check your configuration.");
|
|
25
|
+
}
|
|
26
|
+
try {
|
|
27
|
+
// Dynamically import the EmitKit SDK
|
|
28
|
+
const { EmitKit } = await import("@emitkit/js");
|
|
29
|
+
this.client = new EmitKit({
|
|
30
|
+
apiKey: this.config.apiKey,
|
|
31
|
+
timeout: this.config.timeout ?? DEFAULT_TIMEOUT,
|
|
32
|
+
// The SDK's retries would hold a caller for several timeouts, so a
|
|
33
|
+
// failed request is reported once `timeout` has passed instead.
|
|
34
|
+
maxRetries: 0,
|
|
35
|
+
});
|
|
36
|
+
this.initialized = true;
|
|
37
|
+
this.log("Initialized successfully");
|
|
38
|
+
}
|
|
39
|
+
catch (error) {
|
|
40
|
+
console.error(`[EmitKit-Server] Failed to initialize (${this.getErrorClass(error)})`);
|
|
41
|
+
throw error;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
async identify(userId, traits) {
|
|
45
|
+
if (!this.isEnabled() || !this.initialized || !this.client)
|
|
46
|
+
return;
|
|
47
|
+
// Extract email from traits; EmitKit rejects non-string aliases.
|
|
48
|
+
const email = typeof traits?.email === "string" && traits.email ? traits.email : userId;
|
|
49
|
+
// Build aliases array - EmitKit supports multiple identifiers
|
|
50
|
+
const aliases = [];
|
|
51
|
+
// Add userId as primary alias
|
|
52
|
+
if (userId) {
|
|
53
|
+
aliases.push(userId);
|
|
54
|
+
}
|
|
55
|
+
// Add email if different from userId
|
|
56
|
+
if (email && email !== userId) {
|
|
57
|
+
aliases.push(email);
|
|
58
|
+
}
|
|
59
|
+
// Add any custom alias fields from traits
|
|
60
|
+
if (traits?.username && typeof traits.username === "string") {
|
|
61
|
+
aliases.push(traits.username);
|
|
62
|
+
}
|
|
63
|
+
try {
|
|
64
|
+
await this.client.identify({
|
|
65
|
+
userId,
|
|
66
|
+
// EmitKit merges these into the stored properties.
|
|
67
|
+
...(traits && { properties: asJsonObject(traits) }),
|
|
68
|
+
aliases: aliases.length > 0 ? aliases : undefined,
|
|
69
|
+
});
|
|
70
|
+
this.log("Identified user");
|
|
71
|
+
}
|
|
72
|
+
catch (error) {
|
|
73
|
+
console.error(`[EmitKit-Server] Failed to identify user (${this.describeError(error)})`);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
async track(event, context) {
|
|
77
|
+
if (!this.isEnabled() || !this.initialized || !this.client)
|
|
78
|
+
return;
|
|
79
|
+
// Server providers use only identity supplied on the current call.
|
|
80
|
+
const userId = context?.user?.email || context?.user?.userId || event.userId;
|
|
81
|
+
// Generate event title from action (convert snake_case to Title Case)
|
|
82
|
+
const title = this.formatEventTitle(event.action);
|
|
83
|
+
// Build metadata from event properties and context
|
|
84
|
+
// Strip __emitkit_channel from properties as it's internal routing metadata
|
|
85
|
+
const { __emitkit_channel, ...cleanProperties } = event.properties || {};
|
|
86
|
+
const metadata = {
|
|
87
|
+
...cleanProperties,
|
|
88
|
+
category: event.category,
|
|
89
|
+
timestamp: event.timestamp || Date.now(),
|
|
90
|
+
...(event.sessionId && { sessionId: event.sessionId }),
|
|
91
|
+
...this.getContextMetadata(context),
|
|
92
|
+
};
|
|
93
|
+
// Extract tags from category
|
|
94
|
+
const tags = [];
|
|
95
|
+
if (event.category) {
|
|
96
|
+
tags.push(event.category);
|
|
97
|
+
}
|
|
98
|
+
// Add any custom tags from properties
|
|
99
|
+
if (cleanProperties?.tags &&
|
|
100
|
+
Array.isArray(cleanProperties.tags) &&
|
|
101
|
+
cleanProperties.tags.every((t) => typeof t === "string")) {
|
|
102
|
+
tags.push(...cleanProperties.tags);
|
|
103
|
+
}
|
|
104
|
+
const uniqueTags = [...new Set(tags)];
|
|
105
|
+
// Determine channel name using resolution logic
|
|
106
|
+
const channelName = this.resolveChannelName(event);
|
|
107
|
+
try {
|
|
108
|
+
await this.client.events.create({
|
|
109
|
+
channelName,
|
|
110
|
+
title,
|
|
111
|
+
description: this.getEventDescription(event, context),
|
|
112
|
+
icon: this.getEventIcon(event.category),
|
|
113
|
+
tags: uniqueTags.length > 0 ? uniqueTags : undefined,
|
|
114
|
+
metadata: asJsonObject(metadata),
|
|
115
|
+
userId: userId || null,
|
|
116
|
+
notify: this.config.notify ?? true,
|
|
117
|
+
displayAs: this.config.displayAs || "notification",
|
|
118
|
+
source: EVENT_SOURCE,
|
|
119
|
+
});
|
|
120
|
+
this.log("Tracked event");
|
|
121
|
+
}
|
|
122
|
+
catch (error) {
|
|
123
|
+
console.error(`[EmitKit-Server] Failed to track event (${this.describeError(error)})`);
|
|
124
|
+
throw error;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
async pageView(properties, context) {
|
|
128
|
+
if (!this.isEnabled() || !this.initialized || !this.client)
|
|
129
|
+
return;
|
|
130
|
+
// Page views may only use identity supplied in the current context.
|
|
131
|
+
const userId = context?.user?.email || context?.user?.userId;
|
|
132
|
+
// Strip __emitkit_channel from properties if present
|
|
133
|
+
const { __emitkit_channel, ...cleanProperties } = properties || {};
|
|
134
|
+
// Build page view metadata
|
|
135
|
+
const metadata = {
|
|
136
|
+
...cleanProperties,
|
|
137
|
+
date: new Date().toISOString(),
|
|
138
|
+
...this.getContextMetadata(context),
|
|
139
|
+
};
|
|
140
|
+
// Create a synthetic event for channel resolution
|
|
141
|
+
// Page views use 'navigation' category
|
|
142
|
+
const syntheticEvent = {
|
|
143
|
+
action: "page_view",
|
|
144
|
+
category: "navigation",
|
|
145
|
+
properties: properties || {},
|
|
146
|
+
};
|
|
147
|
+
// Determine channel name using resolution logic
|
|
148
|
+
const channelName = this.resolveChannelName(syntheticEvent);
|
|
149
|
+
try {
|
|
150
|
+
await this.client.events.create({
|
|
151
|
+
channelName,
|
|
152
|
+
title: "Page View",
|
|
153
|
+
description: context?.page?.path || "User viewed a page",
|
|
154
|
+
icon: "👁️",
|
|
155
|
+
tags: ["page_view", "navigation"],
|
|
156
|
+
metadata: asJsonObject(metadata),
|
|
157
|
+
userId: userId || null,
|
|
158
|
+
notify: false, // Don't notify for page views by default
|
|
159
|
+
displayAs: "message",
|
|
160
|
+
source: EVENT_SOURCE,
|
|
161
|
+
});
|
|
162
|
+
this.log("Tracked page view");
|
|
163
|
+
}
|
|
164
|
+
catch (error) {
|
|
165
|
+
console.error(`[EmitKit-Server] Failed to track page view (${this.describeError(error)})`);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
async reset() {
|
|
169
|
+
if (!this.isEnabled() || !this.initialized || !this.client)
|
|
170
|
+
return;
|
|
171
|
+
this.log("Reset called; server provider has no retained identity");
|
|
172
|
+
}
|
|
173
|
+
async shutdown() {
|
|
174
|
+
// EmitKit SDK doesn't require explicit shutdown
|
|
175
|
+
// Events are sent immediately (not batched)
|
|
176
|
+
this.client = undefined;
|
|
177
|
+
this.initialized = false;
|
|
178
|
+
this.log("Shutdown complete");
|
|
179
|
+
}
|
|
180
|
+
// ============================================================================
|
|
181
|
+
// Helper Methods
|
|
182
|
+
// ============================================================================
|
|
183
|
+
/**
|
|
184
|
+
* Request context attached to event metadata. EmitKit shows metadata in
|
|
185
|
+
* team feeds and notifications, so the visitor IP address (which proxy
|
|
186
|
+
* ingestion adds to `device`) is never forwarded.
|
|
187
|
+
*/
|
|
188
|
+
getContextMetadata(context) {
|
|
189
|
+
const device = context?.device && withoutIp(context.device);
|
|
190
|
+
const server = context?.server && withoutIp(context.server);
|
|
191
|
+
return {
|
|
192
|
+
...(context?.page && {
|
|
193
|
+
page: {
|
|
194
|
+
url: context.page.url,
|
|
195
|
+
host: context.page.host,
|
|
196
|
+
path: context.page.path,
|
|
197
|
+
title: context.page.title,
|
|
198
|
+
protocol: context.page.protocol,
|
|
199
|
+
referrer: context.page.referrer,
|
|
200
|
+
...(context.page.search && { search: context.page.search }),
|
|
201
|
+
},
|
|
202
|
+
}),
|
|
203
|
+
...(device && Object.keys(device).length > 0 && { device }),
|
|
204
|
+
...(context?.utm && { utm: context.utm }),
|
|
205
|
+
...(server && Object.keys(server).length > 0 && { server }),
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* Describe an SDK failure for logs. EmitKit errors add their error code,
|
|
210
|
+
* HTTP status, and request id, which tell auth, validation, and rate-limit
|
|
211
|
+
* failures apart; the message and response body are never logged.
|
|
212
|
+
*/
|
|
213
|
+
describeError(error) {
|
|
214
|
+
const errorClass = this.getErrorClass(error);
|
|
215
|
+
try {
|
|
216
|
+
if (!(error instanceof Error) || error.name !== "EmitKitError") {
|
|
217
|
+
return errorClass;
|
|
218
|
+
}
|
|
219
|
+
const code = Reflect.get(error, "code");
|
|
220
|
+
const status = Reflect.get(error, "status");
|
|
221
|
+
const requestId = Reflect.get(error, "requestId");
|
|
222
|
+
return [
|
|
223
|
+
error.name,
|
|
224
|
+
typeof code === "string" ? code : undefined,
|
|
225
|
+
// The SDK reports status 0 when no response arrived.
|
|
226
|
+
typeof status === "number" && status > 0 ? status : undefined,
|
|
227
|
+
typeof requestId === "string" ? `request ${requestId}` : undefined,
|
|
228
|
+
]
|
|
229
|
+
.filter((part) => part !== undefined)
|
|
230
|
+
.join(" ");
|
|
231
|
+
}
|
|
232
|
+
catch {
|
|
233
|
+
// Error description must never replace the original failure.
|
|
234
|
+
return errorClass;
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
/**
|
|
238
|
+
* Format event action into a human-readable title
|
|
239
|
+
* Converts: "user_signed_up" -> "User Signed Up"
|
|
240
|
+
*/
|
|
241
|
+
formatEventTitle(action) {
|
|
242
|
+
return action
|
|
243
|
+
.split("_")
|
|
244
|
+
.map((word) => word.charAt(0).toUpperCase() + word.slice(1))
|
|
245
|
+
.join(" ");
|
|
246
|
+
}
|
|
247
|
+
/**
|
|
248
|
+
* Generate a description for the event
|
|
249
|
+
*/
|
|
250
|
+
getEventDescription(event, context) {
|
|
251
|
+
// Use explicit description from properties if available
|
|
252
|
+
if (event.properties?.description &&
|
|
253
|
+
typeof event.properties.description === "string") {
|
|
254
|
+
return event.properties.description;
|
|
255
|
+
}
|
|
256
|
+
// Generate default description based on category
|
|
257
|
+
const categoryDescriptions = {
|
|
258
|
+
engagement: "User interaction event",
|
|
259
|
+
user: "User lifecycle event",
|
|
260
|
+
navigation: "Navigation event",
|
|
261
|
+
error: "Error or exception occurred",
|
|
262
|
+
performance: "Performance metric",
|
|
263
|
+
conversion: "Conversion event",
|
|
264
|
+
};
|
|
265
|
+
return categoryDescriptions[event.category] || undefined;
|
|
266
|
+
}
|
|
267
|
+
/**
|
|
268
|
+
* Get an appropriate icon for the event category
|
|
269
|
+
*/
|
|
270
|
+
getEventIcon(category) {
|
|
271
|
+
const categoryIcons = {
|
|
272
|
+
engagement: "👆",
|
|
273
|
+
user: "👤",
|
|
274
|
+
navigation: "🧭",
|
|
275
|
+
error: "❌",
|
|
276
|
+
performance: "⚡",
|
|
277
|
+
conversion: "💰",
|
|
278
|
+
};
|
|
279
|
+
return categoryIcons[category];
|
|
280
|
+
}
|
|
281
|
+
/**
|
|
282
|
+
* Resolve the channel name for an event based on priority:
|
|
283
|
+
* 1. Event property __emitkit_channel (highest priority)
|
|
284
|
+
* 2. Category mapping via categoryChannelMap
|
|
285
|
+
* 3. Default channelName (fallback, default: 'general')
|
|
286
|
+
*/
|
|
287
|
+
resolveChannelName(event, defaultChannel) {
|
|
288
|
+
// Priority 1: Check for explicit channel override in properties
|
|
289
|
+
if (event.properties?.__emitkit_channel &&
|
|
290
|
+
typeof event.properties.__emitkit_channel === "string") {
|
|
291
|
+
return event.properties.__emitkit_channel;
|
|
292
|
+
}
|
|
293
|
+
// Priority 2: Check category mapping
|
|
294
|
+
if (this.config.categoryChannelMap && event.category) {
|
|
295
|
+
const mappedChannel = this.config.categoryChannelMap[event.category];
|
|
296
|
+
if (mappedChannel) {
|
|
297
|
+
return mappedChannel;
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
// Priority 3: Use default channel
|
|
301
|
+
return defaultChannel || this.config.channelName || "general";
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
/**
|
|
305
|
+
* The SDK types metadata and properties as JSON values and serializes them
|
|
306
|
+
* with `JSON.stringify`, so trakoo's properties pass through unchanged.
|
|
307
|
+
*/
|
|
308
|
+
function asJsonObject(value) {
|
|
309
|
+
return value;
|
|
310
|
+
}
|
|
311
|
+
function withoutIp(block) {
|
|
312
|
+
const { ip: _ip, ...rest } = block;
|
|
313
|
+
return rest;
|
|
314
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@trakoo/emitkit",
|
|
3
|
+
"version": "0.0.0",
|
|
4
|
+
"description": "EmitKit provider for trakoo",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"exports": {
|
|
7
|
+
"./server": {
|
|
8
|
+
"types": "./dist/server.d.ts",
|
|
9
|
+
"import": "./dist/server.js",
|
|
10
|
+
"default": "./dist/server.js"
|
|
11
|
+
}
|
|
12
|
+
},
|
|
13
|
+
"files": [
|
|
14
|
+
"dist",
|
|
15
|
+
"README.md",
|
|
16
|
+
"LICENSE"
|
|
17
|
+
],
|
|
18
|
+
"sideEffects": false,
|
|
19
|
+
"keywords": [
|
|
20
|
+
"analytics",
|
|
21
|
+
"trakoo",
|
|
22
|
+
"emitkit",
|
|
23
|
+
"typescript"
|
|
24
|
+
],
|
|
25
|
+
"author": "Chris Jayden <https://github.com/multiplehats>",
|
|
26
|
+
"license": "MIT",
|
|
27
|
+
"repository": {
|
|
28
|
+
"type": "git",
|
|
29
|
+
"url": "git+https://github.com/multiplehats/trakoo.git",
|
|
30
|
+
"directory": "packages/emitkit"
|
|
31
|
+
},
|
|
32
|
+
"bugs": {
|
|
33
|
+
"url": "https://github.com/multiplehats/trakoo/issues"
|
|
34
|
+
},
|
|
35
|
+
"homepage": "https://trakoo.co/docs/providers/emitkit",
|
|
36
|
+
"publishConfig": {
|
|
37
|
+
"access": "public"
|
|
38
|
+
},
|
|
39
|
+
"engines": {
|
|
40
|
+
"node": ">=20"
|
|
41
|
+
},
|
|
42
|
+
"peerDependencies": {
|
|
43
|
+
"@emitkit/js": "^3.0.0-next.0",
|
|
44
|
+
"trakoo": "^1.2.1"
|
|
45
|
+
},
|
|
46
|
+
"devDependencies": {
|
|
47
|
+
"@emitkit/js": "^3.0.0-next.0",
|
|
48
|
+
"trakoo": "1.2.1"
|
|
49
|
+
},
|
|
50
|
+
"scripts": {
|
|
51
|
+
"build": "tsc -p tsconfig.build.json",
|
|
52
|
+
"typecheck": "tsc --noEmit",
|
|
53
|
+
"test": "vitest run"
|
|
54
|
+
}
|
|
55
|
+
}
|