mytart 0.1.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/README.md +234 -0
- package/dist/index.d.mts +218 -0
- package/dist/index.d.ts +218 -0
- package/dist/index.js +771 -0
- package/dist/index.mjs +726 -0
- package/package.json +55 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,771 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
|
|
30
|
+
// src/index.ts
|
|
31
|
+
var index_exports = {};
|
|
32
|
+
__export(index_exports, {
|
|
33
|
+
AmplitudeProvider: () => AmplitudeProvider,
|
|
34
|
+
BaseProvider: () => BaseProvider,
|
|
35
|
+
GoogleAnalyticsProvider: () => GoogleAnalyticsProvider,
|
|
36
|
+
MixpanelProvider: () => MixpanelProvider,
|
|
37
|
+
Mytart: () => Mytart,
|
|
38
|
+
PlausibleProvider: () => PlausibleProvider,
|
|
39
|
+
PostHogProvider: () => PostHogProvider,
|
|
40
|
+
SegmentProvider: () => SegmentProvider,
|
|
41
|
+
VercelAnalyticsProvider: () => VercelAnalyticsProvider
|
|
42
|
+
});
|
|
43
|
+
module.exports = __toCommonJS(index_exports);
|
|
44
|
+
|
|
45
|
+
// src/providers/base.ts
|
|
46
|
+
var BaseProvider = class {
|
|
47
|
+
buildError(message, code, originalError) {
|
|
48
|
+
return {
|
|
49
|
+
provider: this.name,
|
|
50
|
+
success: false,
|
|
51
|
+
error: {
|
|
52
|
+
message,
|
|
53
|
+
code,
|
|
54
|
+
provider: this.name,
|
|
55
|
+
originalError
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
buildSuccess(statusCode) {
|
|
60
|
+
return {
|
|
61
|
+
provider: this.name,
|
|
62
|
+
success: true,
|
|
63
|
+
statusCode
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
// src/utils/http.ts
|
|
69
|
+
var import_axios = __toESM(require("axios"));
|
|
70
|
+
function createHttpClient() {
|
|
71
|
+
const instance = import_axios.default.create({
|
|
72
|
+
timeout: 1e4,
|
|
73
|
+
headers: {
|
|
74
|
+
"Content-Type": "application/json"
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
return instance;
|
|
78
|
+
}
|
|
79
|
+
function isAxiosError(error) {
|
|
80
|
+
return import_axios.default.isAxiosError(error);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// src/providers/google-analytics.ts
|
|
84
|
+
var GA4_ENDPOINT = "https://www.google-analytics.com/mp/collect";
|
|
85
|
+
var GA4_DEBUG_ENDPOINT = "https://www.google-analytics.com/debug/mp/collect";
|
|
86
|
+
var GoogleAnalyticsProvider = class extends BaseProvider {
|
|
87
|
+
constructor(config) {
|
|
88
|
+
super();
|
|
89
|
+
this.name = "google-analytics";
|
|
90
|
+
this.config = config;
|
|
91
|
+
this.http = createHttpClient();
|
|
92
|
+
this.endpoint = config.debug ? GA4_DEBUG_ENDPOINT : GA4_ENDPOINT;
|
|
93
|
+
}
|
|
94
|
+
async track({ event, properties, userId, anonymousId, timestamp }) {
|
|
95
|
+
try {
|
|
96
|
+
const clientId = anonymousId ?? this.config.clientId ?? "anonymous";
|
|
97
|
+
const params = { ...properties };
|
|
98
|
+
if (timestamp) {
|
|
99
|
+
params["timestamp_micros"] = timestamp.getTime() * 1e3;
|
|
100
|
+
}
|
|
101
|
+
const body = {
|
|
102
|
+
client_id: clientId,
|
|
103
|
+
events: [{ name: event, params }]
|
|
104
|
+
};
|
|
105
|
+
if (userId) {
|
|
106
|
+
body["user_id"] = userId;
|
|
107
|
+
}
|
|
108
|
+
const response = await this.http.post(this.endpoint, body, {
|
|
109
|
+
params: {
|
|
110
|
+
measurement_id: this.config.measurementId,
|
|
111
|
+
api_secret: this.config.apiSecret
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
return this.buildSuccess(response.status);
|
|
115
|
+
} catch (error) {
|
|
116
|
+
if (isAxiosError(error)) {
|
|
117
|
+
return this.buildError(
|
|
118
|
+
error.message,
|
|
119
|
+
`GA4_HTTP_${error.response?.status ?? "ERROR"}`,
|
|
120
|
+
error
|
|
121
|
+
);
|
|
122
|
+
}
|
|
123
|
+
return this.buildError(String(error), "GA4_UNKNOWN_ERROR", error);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
async identify({ userId, traits }) {
|
|
127
|
+
try {
|
|
128
|
+
const body = {
|
|
129
|
+
client_id: userId,
|
|
130
|
+
user_id: userId,
|
|
131
|
+
user_properties: traits ? Object.fromEntries(
|
|
132
|
+
Object.entries(traits).map(([k, v]) => [k, { value: v }])
|
|
133
|
+
) : void 0,
|
|
134
|
+
events: [{ name: "identify", params: {} }]
|
|
135
|
+
};
|
|
136
|
+
const response = await this.http.post(this.endpoint, body, {
|
|
137
|
+
params: {
|
|
138
|
+
measurement_id: this.config.measurementId,
|
|
139
|
+
api_secret: this.config.apiSecret
|
|
140
|
+
}
|
|
141
|
+
});
|
|
142
|
+
return this.buildSuccess(response.status);
|
|
143
|
+
} catch (error) {
|
|
144
|
+
if (isAxiosError(error)) {
|
|
145
|
+
return this.buildError(
|
|
146
|
+
error.message,
|
|
147
|
+
`GA4_HTTP_${error.response?.status ?? "ERROR"}`,
|
|
148
|
+
error
|
|
149
|
+
);
|
|
150
|
+
}
|
|
151
|
+
return this.buildError(String(error), "GA4_UNKNOWN_ERROR", error);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
async page({ name, url, referrer, userId, anonymousId }) {
|
|
155
|
+
return this.track({
|
|
156
|
+
event: "page_view",
|
|
157
|
+
properties: {
|
|
158
|
+
page_title: name,
|
|
159
|
+
page_location: url,
|
|
160
|
+
page_referrer: referrer
|
|
161
|
+
},
|
|
162
|
+
userId,
|
|
163
|
+
anonymousId
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
// src/providers/mixpanel.ts
|
|
169
|
+
var MIXPANEL_TRACK_ENDPOINT = "https://api.mixpanel.com/track";
|
|
170
|
+
var MIXPANEL_ENGAGE_ENDPOINT = "https://api.mixpanel.com/engage";
|
|
171
|
+
var MixpanelProvider = class extends BaseProvider {
|
|
172
|
+
constructor(config) {
|
|
173
|
+
super();
|
|
174
|
+
this.name = "mixpanel";
|
|
175
|
+
this.config = config;
|
|
176
|
+
this.http = createHttpClient();
|
|
177
|
+
}
|
|
178
|
+
encodeData(payload) {
|
|
179
|
+
const json = JSON.stringify(payload);
|
|
180
|
+
const base64 = Buffer.from(json).toString("base64");
|
|
181
|
+
return base64;
|
|
182
|
+
}
|
|
183
|
+
async track({ event, properties, userId, anonymousId, timestamp }) {
|
|
184
|
+
try {
|
|
185
|
+
const distinctId = userId ?? anonymousId ?? "anonymous";
|
|
186
|
+
const eventPayload = [
|
|
187
|
+
{
|
|
188
|
+
event,
|
|
189
|
+
properties: {
|
|
190
|
+
token: this.config.token,
|
|
191
|
+
distinct_id: distinctId,
|
|
192
|
+
time: timestamp ? Math.floor(timestamp.getTime() / 1e3) : Math.floor(Date.now() / 1e3),
|
|
193
|
+
...properties
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
];
|
|
197
|
+
const endpoint = this.config.apiUrl ?? MIXPANEL_TRACK_ENDPOINT;
|
|
198
|
+
const response = await this.http.post(
|
|
199
|
+
endpoint,
|
|
200
|
+
`data=${this.encodeData(eventPayload)}`,
|
|
201
|
+
{
|
|
202
|
+
headers: { "Content-Type": "application/x-www-form-urlencoded" }
|
|
203
|
+
}
|
|
204
|
+
);
|
|
205
|
+
return this.buildSuccess(response.status);
|
|
206
|
+
} catch (error) {
|
|
207
|
+
if (isAxiosError(error)) {
|
|
208
|
+
return this.buildError(
|
|
209
|
+
error.message,
|
|
210
|
+
`MIXPANEL_HTTP_${error.response?.status ?? "ERROR"}`,
|
|
211
|
+
error
|
|
212
|
+
);
|
|
213
|
+
}
|
|
214
|
+
return this.buildError(String(error), "MIXPANEL_UNKNOWN_ERROR", error);
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
async identify({ userId, traits }) {
|
|
218
|
+
try {
|
|
219
|
+
const engagePayload = [
|
|
220
|
+
{
|
|
221
|
+
$token: this.config.token,
|
|
222
|
+
$distinct_id: userId,
|
|
223
|
+
$set: traits ?? {}
|
|
224
|
+
}
|
|
225
|
+
];
|
|
226
|
+
const endpoint = this.config.apiUrl ? this.config.apiUrl.replace("/track", "/engage") : MIXPANEL_ENGAGE_ENDPOINT;
|
|
227
|
+
const response = await this.http.post(
|
|
228
|
+
endpoint,
|
|
229
|
+
`data=${this.encodeData(engagePayload)}`,
|
|
230
|
+
{
|
|
231
|
+
headers: { "Content-Type": "application/x-www-form-urlencoded" }
|
|
232
|
+
}
|
|
233
|
+
);
|
|
234
|
+
return this.buildSuccess(response.status);
|
|
235
|
+
} catch (error) {
|
|
236
|
+
if (isAxiosError(error)) {
|
|
237
|
+
return this.buildError(
|
|
238
|
+
error.message,
|
|
239
|
+
`MIXPANEL_HTTP_${error.response?.status ?? "ERROR"}`,
|
|
240
|
+
error
|
|
241
|
+
);
|
|
242
|
+
}
|
|
243
|
+
return this.buildError(String(error), "MIXPANEL_UNKNOWN_ERROR", error);
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
async page({ name, url, userId, anonymousId, properties }) {
|
|
247
|
+
return this.track({
|
|
248
|
+
event: "Page View",
|
|
249
|
+
properties: {
|
|
250
|
+
page_name: name,
|
|
251
|
+
url,
|
|
252
|
+
...properties
|
|
253
|
+
},
|
|
254
|
+
userId,
|
|
255
|
+
anonymousId
|
|
256
|
+
});
|
|
257
|
+
}
|
|
258
|
+
};
|
|
259
|
+
|
|
260
|
+
// src/providers/segment.ts
|
|
261
|
+
var SEGMENT_BASE_URL = "https://api.segment.io/v1";
|
|
262
|
+
var SegmentProvider = class extends BaseProvider {
|
|
263
|
+
constructor(config) {
|
|
264
|
+
super();
|
|
265
|
+
this.name = "segment";
|
|
266
|
+
this.config = config;
|
|
267
|
+
this.http = createHttpClient();
|
|
268
|
+
this.baseUrl = config.apiUrl ?? SEGMENT_BASE_URL;
|
|
269
|
+
}
|
|
270
|
+
getAuth() {
|
|
271
|
+
return { username: this.config.writeKey, password: "" };
|
|
272
|
+
}
|
|
273
|
+
async track({ event, properties, userId, anonymousId, timestamp, context }) {
|
|
274
|
+
try {
|
|
275
|
+
const body = {
|
|
276
|
+
event,
|
|
277
|
+
properties: properties ?? {},
|
|
278
|
+
context: context ?? {},
|
|
279
|
+
timestamp: (timestamp ?? /* @__PURE__ */ new Date()).toISOString()
|
|
280
|
+
};
|
|
281
|
+
if (userId) {
|
|
282
|
+
body["userId"] = userId;
|
|
283
|
+
} else {
|
|
284
|
+
body["anonymousId"] = anonymousId ?? "anonymous";
|
|
285
|
+
}
|
|
286
|
+
const response = await this.http.post(`${this.baseUrl}/track`, body, {
|
|
287
|
+
auth: this.getAuth()
|
|
288
|
+
});
|
|
289
|
+
return this.buildSuccess(response.status);
|
|
290
|
+
} catch (error) {
|
|
291
|
+
if (isAxiosError(error)) {
|
|
292
|
+
return this.buildError(
|
|
293
|
+
error.message,
|
|
294
|
+
`SEGMENT_HTTP_${error.response?.status ?? "ERROR"}`,
|
|
295
|
+
error
|
|
296
|
+
);
|
|
297
|
+
}
|
|
298
|
+
return this.buildError(String(error), "SEGMENT_UNKNOWN_ERROR", error);
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
async identify({ userId, traits, anonymousId, timestamp }) {
|
|
302
|
+
try {
|
|
303
|
+
const body = {
|
|
304
|
+
userId,
|
|
305
|
+
traits: traits ?? {},
|
|
306
|
+
timestamp: (timestamp ?? /* @__PURE__ */ new Date()).toISOString()
|
|
307
|
+
};
|
|
308
|
+
if (anonymousId) {
|
|
309
|
+
body["anonymousId"] = anonymousId;
|
|
310
|
+
}
|
|
311
|
+
const response = await this.http.post(`${this.baseUrl}/identify`, body, {
|
|
312
|
+
auth: this.getAuth()
|
|
313
|
+
});
|
|
314
|
+
return this.buildSuccess(response.status);
|
|
315
|
+
} catch (error) {
|
|
316
|
+
if (isAxiosError(error)) {
|
|
317
|
+
return this.buildError(
|
|
318
|
+
error.message,
|
|
319
|
+
`SEGMENT_HTTP_${error.response?.status ?? "ERROR"}`,
|
|
320
|
+
error
|
|
321
|
+
);
|
|
322
|
+
}
|
|
323
|
+
return this.buildError(String(error), "SEGMENT_UNKNOWN_ERROR", error);
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
async page({ name, url, referrer, properties, userId, anonymousId, timestamp }) {
|
|
327
|
+
try {
|
|
328
|
+
const body = {
|
|
329
|
+
name,
|
|
330
|
+
properties: {
|
|
331
|
+
url,
|
|
332
|
+
referrer,
|
|
333
|
+
...properties
|
|
334
|
+
},
|
|
335
|
+
timestamp: (timestamp ?? /* @__PURE__ */ new Date()).toISOString()
|
|
336
|
+
};
|
|
337
|
+
if (userId) {
|
|
338
|
+
body["userId"] = userId;
|
|
339
|
+
} else {
|
|
340
|
+
body["anonymousId"] = anonymousId ?? "anonymous";
|
|
341
|
+
}
|
|
342
|
+
const response = await this.http.post(`${this.baseUrl}/page`, body, {
|
|
343
|
+
auth: this.getAuth()
|
|
344
|
+
});
|
|
345
|
+
return this.buildSuccess(response.status);
|
|
346
|
+
} catch (error) {
|
|
347
|
+
if (isAxiosError(error)) {
|
|
348
|
+
return this.buildError(
|
|
349
|
+
error.message,
|
|
350
|
+
`SEGMENT_HTTP_${error.response?.status ?? "ERROR"}`,
|
|
351
|
+
error
|
|
352
|
+
);
|
|
353
|
+
}
|
|
354
|
+
return this.buildError(String(error), "SEGMENT_UNKNOWN_ERROR", error);
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
};
|
|
358
|
+
|
|
359
|
+
// src/providers/amplitude.ts
|
|
360
|
+
var AMPLITUDE_ENDPOINT = "https://api2.amplitude.com/2/httpapi";
|
|
361
|
+
var AmplitudeProvider = class extends BaseProvider {
|
|
362
|
+
constructor(config) {
|
|
363
|
+
super();
|
|
364
|
+
this.name = "amplitude";
|
|
365
|
+
this.config = config;
|
|
366
|
+
this.http = createHttpClient();
|
|
367
|
+
this.endpoint = config.apiUrl ?? AMPLITUDE_ENDPOINT;
|
|
368
|
+
}
|
|
369
|
+
async track({ event, properties, userId, anonymousId, timestamp }) {
|
|
370
|
+
try {
|
|
371
|
+
const amplitudeEvent = {
|
|
372
|
+
event_type: event,
|
|
373
|
+
event_properties: properties ?? {}
|
|
374
|
+
};
|
|
375
|
+
if (userId) {
|
|
376
|
+
amplitudeEvent["user_id"] = userId;
|
|
377
|
+
} else {
|
|
378
|
+
amplitudeEvent["device_id"] = anonymousId ?? "anonymous";
|
|
379
|
+
}
|
|
380
|
+
if (timestamp) {
|
|
381
|
+
amplitudeEvent["time"] = timestamp.getTime();
|
|
382
|
+
}
|
|
383
|
+
const body = {
|
|
384
|
+
api_key: this.config.apiKey,
|
|
385
|
+
events: [amplitudeEvent]
|
|
386
|
+
};
|
|
387
|
+
const response = await this.http.post(this.endpoint, body);
|
|
388
|
+
return this.buildSuccess(response.status);
|
|
389
|
+
} catch (error) {
|
|
390
|
+
if (isAxiosError(error)) {
|
|
391
|
+
return this.buildError(
|
|
392
|
+
error.message,
|
|
393
|
+
`AMPLITUDE_HTTP_${error.response?.status ?? "ERROR"}`,
|
|
394
|
+
error
|
|
395
|
+
);
|
|
396
|
+
}
|
|
397
|
+
return this.buildError(String(error), "AMPLITUDE_UNKNOWN_ERROR", error);
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
async identify({ userId, traits }) {
|
|
401
|
+
try {
|
|
402
|
+
const amplitudeEvent = {
|
|
403
|
+
event_type: "$identify",
|
|
404
|
+
user_id: userId,
|
|
405
|
+
user_properties: {
|
|
406
|
+
$set: traits ?? {}
|
|
407
|
+
}
|
|
408
|
+
};
|
|
409
|
+
const body = {
|
|
410
|
+
api_key: this.config.apiKey,
|
|
411
|
+
events: [amplitudeEvent]
|
|
412
|
+
};
|
|
413
|
+
const response = await this.http.post(this.endpoint, body);
|
|
414
|
+
return this.buildSuccess(response.status);
|
|
415
|
+
} catch (error) {
|
|
416
|
+
if (isAxiosError(error)) {
|
|
417
|
+
return this.buildError(
|
|
418
|
+
error.message,
|
|
419
|
+
`AMPLITUDE_HTTP_${error.response?.status ?? "ERROR"}`,
|
|
420
|
+
error
|
|
421
|
+
);
|
|
422
|
+
}
|
|
423
|
+
return this.buildError(String(error), "AMPLITUDE_UNKNOWN_ERROR", error);
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
async page({ name, url, userId, anonymousId, properties }) {
|
|
427
|
+
return this.track({
|
|
428
|
+
event: "Page Viewed",
|
|
429
|
+
properties: {
|
|
430
|
+
page_name: name,
|
|
431
|
+
url,
|
|
432
|
+
...properties
|
|
433
|
+
},
|
|
434
|
+
userId,
|
|
435
|
+
anonymousId
|
|
436
|
+
});
|
|
437
|
+
}
|
|
438
|
+
};
|
|
439
|
+
|
|
440
|
+
// src/providers/plausible.ts
|
|
441
|
+
var PLAUSIBLE_ENDPOINT = "https://plausible.io/api/event";
|
|
442
|
+
var PlausibleProvider = class extends BaseProvider {
|
|
443
|
+
constructor(config) {
|
|
444
|
+
super();
|
|
445
|
+
this.name = "plausible";
|
|
446
|
+
this.config = config;
|
|
447
|
+
this.http = createHttpClient();
|
|
448
|
+
this.endpoint = config.apiUrl ?? PLAUSIBLE_ENDPOINT;
|
|
449
|
+
}
|
|
450
|
+
buildHeaders() {
|
|
451
|
+
const headers = {
|
|
452
|
+
"Content-Type": "application/json"
|
|
453
|
+
};
|
|
454
|
+
if (this.config.userAgent) {
|
|
455
|
+
headers["User-Agent"] = this.config.userAgent;
|
|
456
|
+
}
|
|
457
|
+
if (this.config.xForwardedFor) {
|
|
458
|
+
headers["X-Forwarded-For"] = this.config.xForwardedFor;
|
|
459
|
+
}
|
|
460
|
+
return headers;
|
|
461
|
+
}
|
|
462
|
+
async track({ event, properties, context }) {
|
|
463
|
+
try {
|
|
464
|
+
const url = context?.page?.url ?? "";
|
|
465
|
+
const body = {
|
|
466
|
+
name: event,
|
|
467
|
+
url,
|
|
468
|
+
domain: this.config.domain,
|
|
469
|
+
props: properties
|
|
470
|
+
};
|
|
471
|
+
const response = await this.http.post(this.endpoint, body, {
|
|
472
|
+
headers: this.buildHeaders()
|
|
473
|
+
});
|
|
474
|
+
return this.buildSuccess(response.status);
|
|
475
|
+
} catch (error) {
|
|
476
|
+
if (isAxiosError(error)) {
|
|
477
|
+
return this.buildError(
|
|
478
|
+
error.message,
|
|
479
|
+
`PLAUSIBLE_HTTP_${error.response?.status ?? "ERROR"}`,
|
|
480
|
+
error
|
|
481
|
+
);
|
|
482
|
+
}
|
|
483
|
+
return this.buildError(String(error), "PLAUSIBLE_UNKNOWN_ERROR", error);
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
async identify(_options) {
|
|
487
|
+
return {
|
|
488
|
+
provider: this.name,
|
|
489
|
+
success: false,
|
|
490
|
+
error: {
|
|
491
|
+
message: "Plausible does not support user identification",
|
|
492
|
+
code: "PLAUSIBLE_NOT_SUPPORTED",
|
|
493
|
+
provider: this.name
|
|
494
|
+
}
|
|
495
|
+
};
|
|
496
|
+
}
|
|
497
|
+
async page({ name, url, properties }) {
|
|
498
|
+
try {
|
|
499
|
+
const body = {
|
|
500
|
+
name: "pageview",
|
|
501
|
+
url,
|
|
502
|
+
domain: this.config.domain,
|
|
503
|
+
props: {
|
|
504
|
+
page_name: name,
|
|
505
|
+
...properties
|
|
506
|
+
}
|
|
507
|
+
};
|
|
508
|
+
const response = await this.http.post(this.endpoint, body, {
|
|
509
|
+
headers: this.buildHeaders()
|
|
510
|
+
});
|
|
511
|
+
return this.buildSuccess(response.status);
|
|
512
|
+
} catch (error) {
|
|
513
|
+
if (isAxiosError(error)) {
|
|
514
|
+
return this.buildError(
|
|
515
|
+
error.message,
|
|
516
|
+
`PLAUSIBLE_HTTP_${error.response?.status ?? "ERROR"}`,
|
|
517
|
+
error
|
|
518
|
+
);
|
|
519
|
+
}
|
|
520
|
+
return this.buildError(String(error), "PLAUSIBLE_UNKNOWN_ERROR", error);
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
};
|
|
524
|
+
|
|
525
|
+
// src/providers/posthog.ts
|
|
526
|
+
var POSTHOG_ENDPOINT = "https://app.posthog.com/capture/";
|
|
527
|
+
var PostHogProvider = class extends BaseProvider {
|
|
528
|
+
constructor(config) {
|
|
529
|
+
super();
|
|
530
|
+
this.name = "posthog";
|
|
531
|
+
this.config = config;
|
|
532
|
+
this.http = createHttpClient();
|
|
533
|
+
this.endpoint = config.apiUrl ?? POSTHOG_ENDPOINT;
|
|
534
|
+
}
|
|
535
|
+
async track({ event, properties, userId, anonymousId, timestamp }) {
|
|
536
|
+
try {
|
|
537
|
+
const distinctId = userId ?? anonymousId ?? "anonymous";
|
|
538
|
+
const body = {
|
|
539
|
+
api_key: this.config.apiKey,
|
|
540
|
+
event,
|
|
541
|
+
distinct_id: distinctId,
|
|
542
|
+
properties: properties ?? {}
|
|
543
|
+
};
|
|
544
|
+
if (timestamp) {
|
|
545
|
+
body["timestamp"] = timestamp.toISOString();
|
|
546
|
+
}
|
|
547
|
+
const response = await this.http.post(this.endpoint, body);
|
|
548
|
+
return this.buildSuccess(response.status);
|
|
549
|
+
} catch (error) {
|
|
550
|
+
if (isAxiosError(error)) {
|
|
551
|
+
return this.buildError(
|
|
552
|
+
error.message,
|
|
553
|
+
`POSTHOG_HTTP_${error.response?.status ?? "ERROR"}`,
|
|
554
|
+
error
|
|
555
|
+
);
|
|
556
|
+
}
|
|
557
|
+
return this.buildError(String(error), "POSTHOG_UNKNOWN_ERROR", error);
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
async identify({ userId, traits }) {
|
|
561
|
+
try {
|
|
562
|
+
const body = {
|
|
563
|
+
api_key: this.config.apiKey,
|
|
564
|
+
event: "$identify",
|
|
565
|
+
distinct_id: userId,
|
|
566
|
+
properties: {
|
|
567
|
+
$set: traits ?? {}
|
|
568
|
+
}
|
|
569
|
+
};
|
|
570
|
+
const response = await this.http.post(this.endpoint, body);
|
|
571
|
+
return this.buildSuccess(response.status);
|
|
572
|
+
} catch (error) {
|
|
573
|
+
if (isAxiosError(error)) {
|
|
574
|
+
return this.buildError(
|
|
575
|
+
error.message,
|
|
576
|
+
`POSTHOG_HTTP_${error.response?.status ?? "ERROR"}`,
|
|
577
|
+
error
|
|
578
|
+
);
|
|
579
|
+
}
|
|
580
|
+
return this.buildError(String(error), "POSTHOG_UNKNOWN_ERROR", error);
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
async page({ name, url, userId, anonymousId, properties }) {
|
|
584
|
+
return this.track({
|
|
585
|
+
event: "$pageview",
|
|
586
|
+
properties: {
|
|
587
|
+
$current_url: url,
|
|
588
|
+
page_name: name,
|
|
589
|
+
...properties
|
|
590
|
+
},
|
|
591
|
+
userId,
|
|
592
|
+
anonymousId
|
|
593
|
+
});
|
|
594
|
+
}
|
|
595
|
+
};
|
|
596
|
+
|
|
597
|
+
// src/providers/vercel-analytics.ts
|
|
598
|
+
var VERCEL_ENDPOINT = "https://vitals.vercel-insights.com/v1/vitals";
|
|
599
|
+
function generateId() {
|
|
600
|
+
return `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 9)}`;
|
|
601
|
+
}
|
|
602
|
+
function flattenProps(props) {
|
|
603
|
+
const result = {};
|
|
604
|
+
for (const [key, value] of Object.entries(props)) {
|
|
605
|
+
if (typeof value === "string" || typeof value === "number" || typeof value === "boolean" || value === null) {
|
|
606
|
+
result[key] = value;
|
|
607
|
+
} else {
|
|
608
|
+
result[key] = String(value);
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
return result;
|
|
612
|
+
}
|
|
613
|
+
var VercelAnalyticsProvider = class extends BaseProvider {
|
|
614
|
+
constructor(config) {
|
|
615
|
+
super();
|
|
616
|
+
this.name = "vercel-analytics";
|
|
617
|
+
this.config = config;
|
|
618
|
+
this.http = createHttpClient();
|
|
619
|
+
this.endpoint = config.apiUrl ?? VERCEL_ENDPOINT;
|
|
620
|
+
}
|
|
621
|
+
async track({ event, properties, timestamp, context }) {
|
|
622
|
+
try {
|
|
623
|
+
const url = context?.page?.url ?? "";
|
|
624
|
+
const payload = {
|
|
625
|
+
dsn: this.config.analyticsId,
|
|
626
|
+
id: generateId(),
|
|
627
|
+
type: "custom",
|
|
628
|
+
name: event,
|
|
629
|
+
url,
|
|
630
|
+
ts: timestamp ? timestamp.getTime() : Date.now()
|
|
631
|
+
};
|
|
632
|
+
if (properties && Object.keys(properties).length > 0) {
|
|
633
|
+
payload.data = flattenProps(properties);
|
|
634
|
+
}
|
|
635
|
+
const response = await this.http.post(this.endpoint, payload);
|
|
636
|
+
return this.buildSuccess(response.status);
|
|
637
|
+
} catch (error) {
|
|
638
|
+
if (isAxiosError(error)) {
|
|
639
|
+
return this.buildError(
|
|
640
|
+
error.message,
|
|
641
|
+
`VERCEL_ANALYTICS_HTTP_${error.response?.status ?? "ERROR"}`,
|
|
642
|
+
error
|
|
643
|
+
);
|
|
644
|
+
}
|
|
645
|
+
return this.buildError(String(error), "VERCEL_ANALYTICS_UNKNOWN_ERROR", error);
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
/**
|
|
649
|
+
* Vercel Analytics is privacy-focused and does not support traditional user
|
|
650
|
+
* identification. This method returns an unsupported error.
|
|
651
|
+
*/
|
|
652
|
+
async identify(_options) {
|
|
653
|
+
return {
|
|
654
|
+
provider: this.name,
|
|
655
|
+
success: false,
|
|
656
|
+
error: {
|
|
657
|
+
message: "Vercel Analytics does not support user identification",
|
|
658
|
+
code: "VERCEL_ANALYTICS_NOT_SUPPORTED",
|
|
659
|
+
provider: this.name
|
|
660
|
+
}
|
|
661
|
+
};
|
|
662
|
+
}
|
|
663
|
+
async page({ name, url, referrer, properties, timestamp }) {
|
|
664
|
+
try {
|
|
665
|
+
const payload = {
|
|
666
|
+
dsn: this.config.analyticsId,
|
|
667
|
+
id: generateId(),
|
|
668
|
+
type: "pageview",
|
|
669
|
+
name: name ?? "pageview",
|
|
670
|
+
url,
|
|
671
|
+
ts: timestamp ? timestamp.getTime() : Date.now()
|
|
672
|
+
};
|
|
673
|
+
if (referrer) {
|
|
674
|
+
payload.referrer = referrer;
|
|
675
|
+
}
|
|
676
|
+
if (properties && Object.keys(properties).length > 0) {
|
|
677
|
+
return this.track({
|
|
678
|
+
event: name ?? "pageview",
|
|
679
|
+
properties,
|
|
680
|
+
context: { page: { url, referrer } },
|
|
681
|
+
timestamp
|
|
682
|
+
});
|
|
683
|
+
}
|
|
684
|
+
const response = await this.http.post(this.endpoint, payload);
|
|
685
|
+
return this.buildSuccess(response.status);
|
|
686
|
+
} catch (error) {
|
|
687
|
+
if (isAxiosError(error)) {
|
|
688
|
+
return this.buildError(
|
|
689
|
+
error.message,
|
|
690
|
+
`VERCEL_ANALYTICS_HTTP_${error.response?.status ?? "ERROR"}`,
|
|
691
|
+
error
|
|
692
|
+
);
|
|
693
|
+
}
|
|
694
|
+
return this.buildError(String(error), "VERCEL_ANALYTICS_UNKNOWN_ERROR", error);
|
|
695
|
+
}
|
|
696
|
+
}
|
|
697
|
+
};
|
|
698
|
+
|
|
699
|
+
// src/mytart.ts
|
|
700
|
+
function createProvider(config) {
|
|
701
|
+
switch (config.provider) {
|
|
702
|
+
case "google-analytics":
|
|
703
|
+
return new GoogleAnalyticsProvider(config);
|
|
704
|
+
case "mixpanel":
|
|
705
|
+
return new MixpanelProvider(config);
|
|
706
|
+
case "segment":
|
|
707
|
+
return new SegmentProvider(config);
|
|
708
|
+
case "amplitude":
|
|
709
|
+
return new AmplitudeProvider(config);
|
|
710
|
+
case "plausible":
|
|
711
|
+
return new PlausibleProvider(config);
|
|
712
|
+
case "posthog":
|
|
713
|
+
return new PostHogProvider(config);
|
|
714
|
+
case "vercel-analytics":
|
|
715
|
+
return new VercelAnalyticsProvider(config);
|
|
716
|
+
default: {
|
|
717
|
+
const exhaustive = config;
|
|
718
|
+
throw new Error(`Unknown provider: ${exhaustive.provider}`);
|
|
719
|
+
}
|
|
720
|
+
}
|
|
721
|
+
}
|
|
722
|
+
var Mytart = class {
|
|
723
|
+
constructor(config) {
|
|
724
|
+
this.config = config;
|
|
725
|
+
this.providers = config.providers.filter((c) => c.enabled === true).map(createProvider);
|
|
726
|
+
}
|
|
727
|
+
async track(options) {
|
|
728
|
+
const enriched = {
|
|
729
|
+
userId: this.config.defaultUserId,
|
|
730
|
+
anonymousId: this.config.defaultAnonymousId,
|
|
731
|
+
...options
|
|
732
|
+
};
|
|
733
|
+
return Promise.all(this.providers.map((p) => p.track(enriched)));
|
|
734
|
+
}
|
|
735
|
+
async identify(options) {
|
|
736
|
+
return Promise.all(this.providers.map((p) => p.identify(options)));
|
|
737
|
+
}
|
|
738
|
+
async page(options) {
|
|
739
|
+
const enriched = {
|
|
740
|
+
userId: this.config.defaultUserId,
|
|
741
|
+
anonymousId: this.config.defaultAnonymousId,
|
|
742
|
+
...options
|
|
743
|
+
};
|
|
744
|
+
return Promise.all(this.providers.map((p) => p.page(enriched)));
|
|
745
|
+
}
|
|
746
|
+
addProvider(config) {
|
|
747
|
+
if (config.enabled !== true) return;
|
|
748
|
+
this.providers.push(createProvider(config));
|
|
749
|
+
}
|
|
750
|
+
removeProvider(name) {
|
|
751
|
+
const index = this.providers.findIndex((p) => p.name === name);
|
|
752
|
+
if (index !== -1) {
|
|
753
|
+
this.providers.splice(index, 1);
|
|
754
|
+
}
|
|
755
|
+
}
|
|
756
|
+
getProviders() {
|
|
757
|
+
return this.providers.map((p) => p.name);
|
|
758
|
+
}
|
|
759
|
+
};
|
|
760
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
761
|
+
0 && (module.exports = {
|
|
762
|
+
AmplitudeProvider,
|
|
763
|
+
BaseProvider,
|
|
764
|
+
GoogleAnalyticsProvider,
|
|
765
|
+
MixpanelProvider,
|
|
766
|
+
Mytart,
|
|
767
|
+
PlausibleProvider,
|
|
768
|
+
PostHogProvider,
|
|
769
|
+
SegmentProvider,
|
|
770
|
+
VercelAnalyticsProvider
|
|
771
|
+
});
|