@arthurzakharov/convert-service 1.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/README.md +428 -0
- package/dist/app.d.ts +3 -0
- package/dist/app.d.ts.map +1 -0
- package/dist/app.js +37 -0
- package/dist/app.js.map +1 -0
- package/dist/client.d.ts +60 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +207 -0
- package/dist/client.js.map +1 -0
- package/dist/contracts.d.ts +205 -0
- package/dist/contracts.d.ts.map +1 -0
- package/dist/contracts.js +3 -0
- package/dist/contracts.js.map +1 -0
- package/dist/convert/client.d.ts +7 -0
- package/dist/convert/client.d.ts.map +1 -0
- package/dist/convert/client.js +61 -0
- package/dist/convert/client.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +23 -0
- package/dist/index.js.map +1 -0
- package/dist/routes/bucket.d.ts +3 -0
- package/dist/routes/bucket.d.ts.map +1 -0
- package/dist/routes/bucket.js +52 -0
- package/dist/routes/bucket.js.map +1 -0
- package/dist/routes/convert-route-utils.d.ts +20 -0
- package/dist/routes/convert-route-utils.d.ts.map +1 -0
- package/dist/routes/convert-route-utils.js +123 -0
- package/dist/routes/convert-route-utils.js.map +1 -0
- package/dist/routes/experiences.d.ts +3 -0
- package/dist/routes/experiences.d.ts.map +1 -0
- package/dist/routes/experiences.js +120 -0
- package/dist/routes/experiences.js.map +1 -0
- package/dist/routes/features.d.ts +3 -0
- package/dist/routes/features.d.ts.map +1 -0
- package/dist/routes/features.js +126 -0
- package/dist/routes/features.js.map +1 -0
- package/dist/routes/health.d.ts +3 -0
- package/dist/routes/health.d.ts.map +1 -0
- package/dist/routes/health.js +16 -0
- package/dist/routes/health.js.map +1 -0
- package/dist/routes/track.d.ts +3 -0
- package/dist/routes/track.d.ts.map +1 -0
- package/dist/routes/track.js +37 -0
- package/dist/routes/track.js.map +1 -0
- package/dist/utils/logger.d.ts +11 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +136 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/segments.d.ts +25 -0
- package/dist/utils/segments.d.ts.map +1 -0
- package/dist/utils/segments.js +103 -0
- package/dist/utils/segments.js.map +1 -0
- package/package.json +60 -0
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// ── ANSI escape helpers ───────────────────────────────────────────────────────
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
exports.Logger = void 0;
|
|
5
|
+
exports.createLogger = createLogger;
|
|
6
|
+
const R = '\x1b[0m'; // reset
|
|
7
|
+
const BOLD = '\x1b[1m';
|
|
8
|
+
const DIM = '\x1b[2m';
|
|
9
|
+
const fg = {
|
|
10
|
+
gray: '\x1b[90m',
|
|
11
|
+
red: '\x1b[91m',
|
|
12
|
+
green: '\x1b[92m',
|
|
13
|
+
yellow: '\x1b[93m',
|
|
14
|
+
blue: '\x1b[94m',
|
|
15
|
+
magenta: '\x1b[95m',
|
|
16
|
+
cyan: '\x1b[96m',
|
|
17
|
+
white: '\x1b[97m',
|
|
18
|
+
};
|
|
19
|
+
const bg = {
|
|
20
|
+
blue: '\x1b[44m',
|
|
21
|
+
yellow: '\x1b[43m',
|
|
22
|
+
red: '\x1b[41m',
|
|
23
|
+
gray: '\x1b[100m',
|
|
24
|
+
green: '\x1b[42m',
|
|
25
|
+
};
|
|
26
|
+
// Always use color unless the NO_COLOR convention is set.
|
|
27
|
+
// Railway (and most modern log viewers) renders ANSI fine even though
|
|
28
|
+
// process.stdout.isTTY is false in piped/containerised environments.
|
|
29
|
+
const USE_COLOR = !process.env['NO_COLOR'];
|
|
30
|
+
const paint = (code, text) => USE_COLOR ? `${code}${text}${R}` : text;
|
|
31
|
+
const bold = (s) => paint(BOLD, s);
|
|
32
|
+
const dim = (s) => paint(DIM, s);
|
|
33
|
+
const gray = (s) => paint(fg.gray, s);
|
|
34
|
+
const green = (s) => paint(fg.green, s);
|
|
35
|
+
const yellow = (s) => paint(fg.yellow, s);
|
|
36
|
+
const cyan = (s) => paint(fg.cyan, s);
|
|
37
|
+
const blue = (s) => paint(fg.blue, s);
|
|
38
|
+
const magenta = (s) => paint(fg.magenta, s);
|
|
39
|
+
const white = (s) => paint(fg.white, s);
|
|
40
|
+
// ── Value formatting ──────────────────────────────────────────────────────────
|
|
41
|
+
const MAX_DEPTH = 4;
|
|
42
|
+
const TREE_INDENT = ' ';
|
|
43
|
+
function isPrimitive(val) {
|
|
44
|
+
return (val === null ||
|
|
45
|
+
val === undefined ||
|
|
46
|
+
(typeof val !== 'object' && typeof val !== 'function'));
|
|
47
|
+
}
|
|
48
|
+
function formatPrimitive(val) {
|
|
49
|
+
if (val === null)
|
|
50
|
+
return dim('null');
|
|
51
|
+
if (val === undefined)
|
|
52
|
+
return dim('undefined');
|
|
53
|
+
if (typeof val === 'string')
|
|
54
|
+
return green(`'${val}'`);
|
|
55
|
+
if (typeof val === 'number')
|
|
56
|
+
return yellow(String(val));
|
|
57
|
+
if (typeof val === 'boolean')
|
|
58
|
+
return cyan(String(val));
|
|
59
|
+
return String(val);
|
|
60
|
+
}
|
|
61
|
+
function formatValue(val, depth = 0, baseIndent = '') {
|
|
62
|
+
if (isPrimitive(val))
|
|
63
|
+
return formatPrimitive(val);
|
|
64
|
+
if (Array.isArray(val)) {
|
|
65
|
+
if (val.length === 0)
|
|
66
|
+
return dim('[]');
|
|
67
|
+
// Flat array of primitives → compact inline
|
|
68
|
+
if (val.every(isPrimitive)) {
|
|
69
|
+
const items = val.map(formatPrimitive).join(dim(', '));
|
|
70
|
+
return `${dim('[')} ${items} ${dim(']')}`;
|
|
71
|
+
}
|
|
72
|
+
// Mixed / nested → tree
|
|
73
|
+
return formatEntries(val.map((v, i) => [String(i), v]), depth, baseIndent);
|
|
74
|
+
}
|
|
75
|
+
if (typeof val === 'object' && val !== null) {
|
|
76
|
+
if (depth >= MAX_DEPTH)
|
|
77
|
+
return gray('{…}');
|
|
78
|
+
const entries = Object.entries(val);
|
|
79
|
+
if (entries.length === 0)
|
|
80
|
+
return dim('{}');
|
|
81
|
+
return formatEntries(entries, depth, baseIndent);
|
|
82
|
+
}
|
|
83
|
+
return String(val);
|
|
84
|
+
}
|
|
85
|
+
function formatEntries(entries, depth, baseIndent) {
|
|
86
|
+
const childIndent = baseIndent + TREE_INDENT;
|
|
87
|
+
const lines = entries.map(([key, val], i) => {
|
|
88
|
+
const isLast = i === entries.length - 1;
|
|
89
|
+
const branch = dim(isLast ? '└─ ' : '├─ ');
|
|
90
|
+
const keyPart = blue(key) + dim(':');
|
|
91
|
+
const valStr = formatValue(val, depth + 1, childIndent + TREE_INDENT);
|
|
92
|
+
// If the formatted value itself starts with '\n' it already carries its own tree
|
|
93
|
+
const sep = valStr.startsWith('\n') ? '' : ' ';
|
|
94
|
+
return `${childIndent}${branch}${keyPart}${sep}${valStr}`;
|
|
95
|
+
});
|
|
96
|
+
return '\n' + lines.join('\n');
|
|
97
|
+
}
|
|
98
|
+
const BADGE = {
|
|
99
|
+
info: paint(`${BOLD}${bg.blue}`, white(' INFO ')),
|
|
100
|
+
warn: paint(`${BOLD}${bg.yellow}`, white(' WARN ')),
|
|
101
|
+
error: paint(`${BOLD}${bg.red}`, white(' ERR ')),
|
|
102
|
+
debug: paint(`${BOLD}${bg.gray}`, white(' DBG ')),
|
|
103
|
+
};
|
|
104
|
+
// ── Logger ────────────────────────────────────────────────────────────────────
|
|
105
|
+
function timestamp() {
|
|
106
|
+
return dim(new Date().toTimeString().slice(0, 8));
|
|
107
|
+
}
|
|
108
|
+
class Logger {
|
|
109
|
+
constructor(ns) {
|
|
110
|
+
this.ns = ns;
|
|
111
|
+
}
|
|
112
|
+
emit(level, message, data) {
|
|
113
|
+
const ns = magenta(this.ns);
|
|
114
|
+
const header = `${timestamp()} ${BADGE[level]} ${ns} ${bold(message)}`;
|
|
115
|
+
if (data === undefined) {
|
|
116
|
+
process.stdout.write(header + '\n');
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
if (isPrimitive(data)) {
|
|
120
|
+
process.stdout.write(`${header} ${formatPrimitive(data)}\n`);
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
const tree = formatValue(data, 0, '');
|
|
124
|
+
process.stdout.write(header + tree + '\n');
|
|
125
|
+
}
|
|
126
|
+
info(msg, data) { this.emit('info', msg, data); }
|
|
127
|
+
warn(msg, data) { this.emit('warn', msg, data); }
|
|
128
|
+
error(msg, data) { this.emit('error', msg, data); }
|
|
129
|
+
debug(msg, data) { this.emit('debug', msg, data); }
|
|
130
|
+
}
|
|
131
|
+
exports.Logger = Logger;
|
|
132
|
+
// ── Factory ───────────────────────────────────────────────────────────────────
|
|
133
|
+
function createLogger(namespace) {
|
|
134
|
+
return new Logger(namespace);
|
|
135
|
+
}
|
|
136
|
+
//# sourceMappingURL=logger.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logger.js","sourceRoot":"","sources":["../../src/utils/logger.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { Request } from 'express';
|
|
2
|
+
type ConvertBrowser = 'IE' | 'CH' | 'FF' | 'OP' | 'SF' | 'OTH';
|
|
3
|
+
type ConvertDevice = 'ALLPH' | 'IPH' | 'OTHPH' | 'ALLTAB' | 'IPAD' | 'OTHTAB' | 'DESK' | 'OTHDEV';
|
|
4
|
+
type ConvertSource = 'campaign' | 'search' | 'referral' | 'direct' | 'ai_tool';
|
|
5
|
+
type ConvertVisitorType = 'new' | 'returning';
|
|
6
|
+
export interface VisitorSegments {
|
|
7
|
+
browser?: ConvertBrowser;
|
|
8
|
+
devices?: ConvertDevice[];
|
|
9
|
+
country?: string;
|
|
10
|
+
source?: ConvertSource;
|
|
11
|
+
campaign?: string;
|
|
12
|
+
visitorType?: ConvertVisitorType;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Build Convert default segments from the incoming HTTP request.
|
|
16
|
+
*
|
|
17
|
+
* Auto-detected from headers: browser, devices, country (geoip from IP)
|
|
18
|
+
* Passed by frontend: pageUrl (not used directly), campaign (UTM param)
|
|
19
|
+
*
|
|
20
|
+
* Note: Convert's `source` field is a traffic category ('direct' | 'campaign' | ...),
|
|
21
|
+
* not a URL. If a UTM campaign is present we report 'campaign'; otherwise 'direct'.
|
|
22
|
+
*/
|
|
23
|
+
export declare function buildSegments(req: Request, _pageUrl?: string, campaign?: string): VisitorSegments;
|
|
24
|
+
export {};
|
|
25
|
+
//# sourceMappingURL=segments.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"segments.d.ts","sourceRoot":"","sources":["../../src/utils/segments.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAMvC,KAAK,cAAc,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,KAAK,CAAC;AAC/D,KAAK,aAAa,GAAG,OAAO,GAAG,KAAK,GAAG,OAAO,GAAG,QAAQ,GAAG,MAAM,GAAG,QAAQ,GAAG,MAAM,GAAG,QAAQ,CAAC;AAClG,KAAK,aAAa,GAAG,UAAU,GAAG,QAAQ,GAAG,UAAU,GAAG,QAAQ,GAAG,SAAS,CAAC;AAC/E,KAAK,kBAAkB,GAAG,KAAK,GAAG,WAAW,CAAC;AAE9C,MAAM,WAAW,eAAe;IAC9B,OAAO,CAAC,EAAE,cAAc,CAAC;IACzB,OAAO,CAAC,EAAE,aAAa,EAAE,CAAC;IAC1B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,aAAa,CAAC;IACvB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,kBAAkB,CAAC;CAClC;AAqDD;;;;;;;;GAQG;AACH,wBAAgB,aAAa,CAC3B,GAAG,EAAE,OAAO,EACZ,QAAQ,CAAC,EAAE,MAAM,EACjB,QAAQ,CAAC,EAAE,MAAM,GAChB,eAAe,CA6BjB"}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.buildSegments = buildSegments;
|
|
7
|
+
const ua_parser_js_1 = require("ua-parser-js");
|
|
8
|
+
const geoip_lite_1 = __importDefault(require("geoip-lite"));
|
|
9
|
+
const logger_1 = require("./logger");
|
|
10
|
+
const log = (0, logger_1.createLogger)('segments');
|
|
11
|
+
/**
|
|
12
|
+
* Normalise UAParser browser name to Convert's internal browser codes.
|
|
13
|
+
* Convert API accepts: 'CH' (Chrome) | 'FF' (Firefox) | 'SF' (Safari) |
|
|
14
|
+
* 'IE' (IE) | 'OP' (Opera) | 'OTH' (Other)
|
|
15
|
+
* Note: Edge is not in the enum — maps to 'OTH'.
|
|
16
|
+
* Order matters: check 'edge' before 'chrome' (Edge UA contains "Chrome")
|
|
17
|
+
*/
|
|
18
|
+
function normaliseBrowser(name) {
|
|
19
|
+
if (!name)
|
|
20
|
+
return 'OTH';
|
|
21
|
+
const n = name.toLowerCase();
|
|
22
|
+
if (n.includes('edge'))
|
|
23
|
+
return 'OTH';
|
|
24
|
+
if (n.includes('chrome') || n.includes('chromium'))
|
|
25
|
+
return 'CH';
|
|
26
|
+
if (n.includes('firefox'))
|
|
27
|
+
return 'FF';
|
|
28
|
+
if (n.includes('safari'))
|
|
29
|
+
return 'SF';
|
|
30
|
+
if (n.includes('ie') || n.includes('internet explorer'))
|
|
31
|
+
return 'IE';
|
|
32
|
+
if (n.includes('opera'))
|
|
33
|
+
return 'OP';
|
|
34
|
+
return 'OTH';
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Normalise UAParser device type to Convert's internal device codes (array).
|
|
38
|
+
* Convert API accepts: ['DESK'] | ['ALLPH'] | ['ALLTAB']
|
|
39
|
+
* UAParser returns undefined for desktops — treat that as 'DESK'.
|
|
40
|
+
*/
|
|
41
|
+
function normaliseDevice(type) {
|
|
42
|
+
if (type === 'mobile')
|
|
43
|
+
return ['ALLPH'];
|
|
44
|
+
if (type === 'tablet')
|
|
45
|
+
return ['ALLTAB'];
|
|
46
|
+
return ['DESK'];
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Derive traffic source category from available context.
|
|
50
|
+
* Convert API accepts: 'campaign' | 'search' | 'referral' | 'direct' | 'ai_tool'
|
|
51
|
+
*/
|
|
52
|
+
function deriveSource(campaign) {
|
|
53
|
+
if (campaign)
|
|
54
|
+
return 'campaign';
|
|
55
|
+
return 'direct';
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Extract the real client IP, accounting for Railway / reverse proxy headers.
|
|
59
|
+
*/
|
|
60
|
+
function getClientIp(req) {
|
|
61
|
+
const forwarded = req.headers['x-forwarded-for'];
|
|
62
|
+
if (forwarded) {
|
|
63
|
+
const first = Array.isArray(forwarded) ? forwarded[0] : forwarded.split(',')[0];
|
|
64
|
+
return first.trim();
|
|
65
|
+
}
|
|
66
|
+
return req.socket?.remoteAddress;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Build Convert default segments from the incoming HTTP request.
|
|
70
|
+
*
|
|
71
|
+
* Auto-detected from headers: browser, devices, country (geoip from IP)
|
|
72
|
+
* Passed by frontend: pageUrl (not used directly), campaign (UTM param)
|
|
73
|
+
*
|
|
74
|
+
* Note: Convert's `source` field is a traffic category ('direct' | 'campaign' | ...),
|
|
75
|
+
* not a URL. If a UTM campaign is present we report 'campaign'; otherwise 'direct'.
|
|
76
|
+
*/
|
|
77
|
+
function buildSegments(req, _pageUrl, campaign) {
|
|
78
|
+
const ua = req.headers['user-agent'];
|
|
79
|
+
const parser = new ua_parser_js_1.UAParser(ua);
|
|
80
|
+
const result = parser.getResult();
|
|
81
|
+
// Country: try Cloudflare header first, fall back to offline geoip lookup
|
|
82
|
+
let country;
|
|
83
|
+
const cfCountry = req.headers['cf-ipcountry'];
|
|
84
|
+
if (cfCountry && cfCountry !== 'XX') {
|
|
85
|
+
country = cfCountry;
|
|
86
|
+
}
|
|
87
|
+
else {
|
|
88
|
+
const ip = getClientIp(req);
|
|
89
|
+
if (ip) {
|
|
90
|
+
const geo = geoip_lite_1.default.lookup(ip);
|
|
91
|
+
country = geo?.country ?? undefined;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
const browser = normaliseBrowser(result.browser.name);
|
|
95
|
+
const devices = normaliseDevice(result.device.type);
|
|
96
|
+
const source = deriveSource(campaign);
|
|
97
|
+
const segments = { browser, devices, country, source };
|
|
98
|
+
if (campaign)
|
|
99
|
+
segments.campaign = campaign;
|
|
100
|
+
log.debug('built', { browser, devices, country, source, campaign });
|
|
101
|
+
return segments;
|
|
102
|
+
}
|
|
103
|
+
//# sourceMappingURL=segments.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"segments.js","sourceRoot":"","sources":["../../src/utils/segments.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@arthurzakharov/convert-service",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Server-side Convert.com SDK proxy service",
|
|
5
|
+
"repository": {
|
|
6
|
+
"url": "https://github.com/arthurzakharov/convert-service"
|
|
7
|
+
},
|
|
8
|
+
"author": {
|
|
9
|
+
"name": "Artur Zakharov",
|
|
10
|
+
"email": "zakharov.arthur@icloud.com"
|
|
11
|
+
},
|
|
12
|
+
"publishConfig": {
|
|
13
|
+
"access": "public"
|
|
14
|
+
},
|
|
15
|
+
"main": "dist/index.js",
|
|
16
|
+
"types": "dist/index.d.ts",
|
|
17
|
+
"exports": {
|
|
18
|
+
".": {
|
|
19
|
+
"types": "./dist/index.d.ts",
|
|
20
|
+
"default": "./dist/index.js"
|
|
21
|
+
},
|
|
22
|
+
"./client": {
|
|
23
|
+
"types": "./dist/client.d.ts",
|
|
24
|
+
"default": "./dist/client.js"
|
|
25
|
+
},
|
|
26
|
+
"./contracts": {
|
|
27
|
+
"types": "./dist/contracts.d.ts",
|
|
28
|
+
"default": "./dist/contracts.js"
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
"files": [
|
|
32
|
+
"dist",
|
|
33
|
+
"README.md"
|
|
34
|
+
],
|
|
35
|
+
"scripts": {
|
|
36
|
+
"build": "tsc && tsc-alias && node scripts/build-report.js",
|
|
37
|
+
"start": "node dist/index.js",
|
|
38
|
+
"dev": "ts-node-dev -r tsconfig-paths/register --respawn --transpile-only src/index.ts",
|
|
39
|
+
"typecheck": "tsc --noEmit"
|
|
40
|
+
},
|
|
41
|
+
"dependencies": {
|
|
42
|
+
"@convertcom/js-sdk": "^4.4.3",
|
|
43
|
+
"cors": "^2.8.5",
|
|
44
|
+
"dotenv": "^16.4.5",
|
|
45
|
+
"express": "^4.19.2",
|
|
46
|
+
"geoip-lite": "^1.4.10",
|
|
47
|
+
"ua-parser-js": "^2.0.10"
|
|
48
|
+
},
|
|
49
|
+
"devDependencies": {
|
|
50
|
+
"@types/cors": "^2.8.17",
|
|
51
|
+
"@types/express": "^4.17.21",
|
|
52
|
+
"@types/geoip-lite": "^1.4.4",
|
|
53
|
+
"@types/node": "^20.14.0",
|
|
54
|
+
"@types/ua-parser-js": "^0.7.39",
|
|
55
|
+
"ts-node-dev": "^2.0.0",
|
|
56
|
+
"tsc-alias": "^1.8.17",
|
|
57
|
+
"tsconfig-paths": "^4.2.0",
|
|
58
|
+
"typescript": "^5.4.5"
|
|
59
|
+
}
|
|
60
|
+
}
|