@jitsu/functions-lib
Advanced tools
Comparing version
'use strict'; | ||
const ACode = "A".charCodeAt(0); | ||
const ZCode = "Z".charCodeAt(0); | ||
const aCode = "a".charCodeAt(0); | ||
const zCode = "z".charCodeAt(0); | ||
const zeroCode = "0".charCodeAt(0); | ||
const nineCode = "9".charCodeAt(0); | ||
const spaceCode = " ".charCodeAt(0); | ||
const underscodeCode = "_".charCodeAt(0); | ||
function idToSnakeCaseFast(id) { | ||
let res = ""; | ||
let concatIndex = 0; | ||
let i = 0; | ||
let needUnderscore = false; | ||
for (; i < id.length; i++) { | ||
const c = id.charCodeAt(i); | ||
if (c >= ACode && c <= ZCode) { | ||
res += id.substring(concatIndex, i) + (needUnderscore ? "_" : "") + id.charAt(i).toLowerCase(); | ||
concatIndex = i + 1; | ||
} | ||
else if (c == spaceCode) { | ||
res += id.substring(concatIndex, i) + "_"; | ||
concatIndex = i + 1; | ||
} | ||
// needUnderscore is used in case next char is a capital latin letter | ||
// we add underscore only between latin letters | ||
needUnderscore = (c >= aCode && c <= zCode) || (c >= ACode && c <= ZCode); | ||
} | ||
if (concatIndex == 0) { | ||
return id; | ||
} | ||
else if (concatIndex < i) { | ||
res += id.substring(concatIndex, i); | ||
} | ||
return res; | ||
} | ||
function idToClassic(id) { | ||
let needConvert = false; | ||
for (let i = 0; i < id.length; i++) { | ||
const c = id.charCodeAt(i); | ||
if (!((c >= aCode && c <= zCode) || (c >= zeroCode && c <= nineCode) || c == underscodeCode)) { | ||
needConvert = true; | ||
break; | ||
} | ||
} | ||
if (!needConvert) { | ||
return id; | ||
} | ||
let res = ""; | ||
for (let i = 0; i < id.length; i++) { | ||
const c = id.charCodeAt(i); | ||
if (c >= ACode && c <= ZCode) { | ||
res += id.charAt(i).toLowerCase(); | ||
} | ||
else if ((c >= aCode && c <= zCode) || (c >= zeroCode && c <= nineCode)) { | ||
res += id.charAt(i); | ||
} | ||
else { | ||
res += "_"; | ||
} | ||
} | ||
return res; | ||
} | ||
function toSnakeCase(param) { | ||
if (Array.isArray(param)) { | ||
return param.map(toSnakeCase); | ||
} | ||
else if (typeof param === "object" && param !== null) { | ||
const r = {}; | ||
for (const [key, value] of Object.entries(param)) { | ||
r[idToSnakeCaseFast(key)] = toSnakeCase(value); | ||
} | ||
return r; | ||
} | ||
else { | ||
return param; | ||
} | ||
} | ||
function toClassic(param) { | ||
if (Array.isArray(param)) { | ||
return param.map(toClassic); | ||
} | ||
else if (typeof param === "object" && param !== null) { | ||
const r = {}; | ||
for (const [key, value] of Object.entries(param)) { | ||
r[idToClassic(key)] = toClassic(value); | ||
} | ||
return r; | ||
} | ||
else { | ||
return param; | ||
} | ||
} | ||
function removeUndefined(param) { | ||
if (Array.isArray(param)) { | ||
return param.map(removeUndefined); | ||
} | ||
else if (typeof param === "object" && param !== null) { | ||
for (const [key, value] of Object.entries(param)) { | ||
switch (typeof value) { | ||
case "undefined": | ||
delete param[key]; | ||
break; | ||
case "object": | ||
if (value !== null) { | ||
removeUndefined(value); | ||
} | ||
break; | ||
} | ||
} | ||
} | ||
return param; | ||
} | ||
function transferAsSnakeCase(target, source, omit) { | ||
if (typeof source !== "object") { | ||
return; | ||
} | ||
for (const [k, v] of Object.entries(source)) { | ||
if (!omit || !omit.includes(k)) { | ||
target[idToSnakeCaseFast(k)] = toSnakeCase(v); | ||
} | ||
} | ||
} | ||
function transferAsClassic(target, source, omit) { | ||
if (typeof source !== "object") { | ||
return; | ||
} | ||
for (const [k, v] of Object.entries(source)) { | ||
if (!omit || !omit.includes(k)) { | ||
target[idToClassic(k)] = toClassic(v); | ||
} | ||
} | ||
} | ||
function transferValueAsSnakeCase(target, property, source) { | ||
if (typeof source === "undefined") { | ||
return; | ||
} | ||
target[property] = toSnakeCase(source); | ||
} | ||
function transfer(target, source, omit) { | ||
if (typeof source !== "object") { | ||
return; | ||
} | ||
for (const [k, v] of Object.entries(source)) { | ||
if (!omit || !omit.includes(k)) { | ||
target[k] = v; | ||
} | ||
} | ||
} | ||
function transferValue(target, property, source) { | ||
if (typeof source === "undefined") { | ||
return; | ||
} | ||
target[property] = source; | ||
} | ||
const TableNameParameter = "JITSU_TABLE_NAME"; | ||
const DropRetryErrorName = "Drop & RetryError"; | ||
@@ -51,2 +208,278 @@ const RetryErrorName = "RetryError"; | ||
} | ||
function anonymizeIp(ip) { | ||
if (!ip) { | ||
return; | ||
} | ||
const parts = ip.split("."); | ||
if (parts.length === 4) { | ||
return `${parts[0]}.${parts[1]}.${parts[2]}.0`; | ||
} | ||
} | ||
function toJitsuClassic(event, ctx) { | ||
const keepOriginalNames = !!ctx.props.keepOriginalNames; | ||
const fileStorage = ctx.destination.type === "s3" || ctx.destination.type === "gcs"; | ||
let transferFunc = transferAsSnakeCase; | ||
if (keepOriginalNames) { | ||
if (fileStorage) { | ||
transferFunc = transfer; | ||
} | ||
else { | ||
transferFunc = transferAsClassic; | ||
} | ||
} | ||
let url = undefined; | ||
const analyticsContext = event.context || {}; | ||
const urlStr = analyticsContext.page?.url || event.properties?.url; | ||
try { | ||
if (urlStr) { | ||
url = new URL(urlStr); | ||
} | ||
} | ||
catch (e) { } | ||
const click_id = {}; | ||
transferFunc(click_id, analyticsContext.clientIds, ["ga4", "fbp", "fbc"]); | ||
let ids = {}; | ||
if (Object.keys(analyticsContext.clientIds || {}).length > 0) { | ||
ids = removeUndefined({ | ||
ga: analyticsContext.clientIds.ga4?.clientId, | ||
fbp: analyticsContext.clientIds.fbp, | ||
fbc: analyticsContext.clientIds.fbc, | ||
}); | ||
} | ||
const geo = analyticsContext.geo || {}; | ||
const ua = ctx?.ua || {}; | ||
const user = removeUndefined({ | ||
id: event.userId, | ||
anonymous_id: event.anonymousId, | ||
email: (analyticsContext.traits?.email || event.traits?.email || undefined), | ||
name: (analyticsContext.traits?.name || event.traits?.name || undefined), | ||
}); | ||
transferFunc(user, analyticsContext.traits, ["email", "name"]); | ||
transferFunc(user, event.traits, ["email", "name"]); | ||
const classic = { | ||
[TableNameParameter]: event[TableNameParameter], | ||
anon_ip: analyticsContext.ip ? anonymizeIp(analyticsContext.ip) : undefined, | ||
api_key: event.writeKey || "", | ||
click_id: Object.keys(click_id).length > 0 ? click_id : undefined, | ||
doc_encoding: analyticsContext.page?.encoding ?? event.properties?.encoding, | ||
doc_host: (analyticsContext.page?.host ?? event.properties?.host) || url?.host, | ||
doc_path: (analyticsContext.page?.path ?? event.properties?.path) || url?.pathname, | ||
doc_search: (analyticsContext.page?.search ?? event.properties?.search) || url?.search, | ||
eventn_ctx_event_id: event.messageId, | ||
event_type: event.event || event.type, | ||
local_tz_offset: analyticsContext.page?.timezoneOffset ?? event.properties?.timezoneOffset, | ||
page_title: analyticsContext.page?.title, | ||
referer: analyticsContext.page?.referrer, | ||
screen_resolution: Object.keys(analyticsContext.screen || {}).length > 0 | ||
? Math.max(analyticsContext.screen.width || 0) + "x" + Math.max(analyticsContext.screen.height || 0) | ||
: undefined, | ||
source_ip: analyticsContext.ip, | ||
src: event.properties?.src || "jitsu", | ||
url: urlStr, | ||
user: Object.keys(user).length > 0 ? user : undefined, | ||
location: Object.keys(geo).length > 0 | ||
? { | ||
city: geo.city?.name, | ||
continent: geo.continent?.name, | ||
country: geo.country?.code, | ||
country_name: geo.country?.name, | ||
latitude: geo.location?.latitude, | ||
longitude: geo.location?.longitude, | ||
region: geo.region?.code, | ||
zip: geo.postalCode?.code, | ||
timezone: geo.location?.timezone, | ||
autonomous_system_number: geo.provider?.as?.num, | ||
autonomous_system_organization: geo.provider?.as?.name, | ||
isp: geo.provider?.isp, | ||
domain: geo.provider?.domain, | ||
} | ||
: undefined, | ||
ids: Object.keys(ids).length > 0 ? ids : undefined, | ||
parsed_ua: Object.keys(ua).length > 0 | ||
? { | ||
os_family: ua.os?.name, | ||
os_version: ua.os?.version, | ||
ua_family: ua.browser?.name, | ||
ua_version: ua.browser?.version, | ||
device_brand: ua.device?.vendor, | ||
device_type: ua.device?.type, | ||
device_model: ua.device?.model, | ||
bot: ua.bot, | ||
} | ||
: undefined, | ||
user_agent: analyticsContext.userAgent, | ||
user_language: analyticsContext.locale, | ||
utc_time: event.timestamp, | ||
_timestamp: event.receivedAt, | ||
utm: analyticsContext.campaign, | ||
vp_size: Object.keys(analyticsContext.screen || {}).length > 0 | ||
? Math.max(analyticsContext.screen.innerWidth || 0) + "x" + Math.max(analyticsContext.screen.innerHeight || 0) | ||
: undefined, | ||
}; | ||
if (event.type === "track") { | ||
transferFunc(classic, event.properties); | ||
} | ||
else { | ||
transferFunc(classic, event.properties, ["url", "title", "referrer", "search", "host", "path", "width", "height"]); | ||
} | ||
return removeUndefined(classic); | ||
} | ||
function fromJitsuClassic(event) { | ||
let type = "track"; | ||
let eventName = undefined; | ||
switch ((event.event_type ?? "").toLowerCase()) { | ||
case "pageview": | ||
case "page_view": | ||
case "page": | ||
type = "page"; | ||
eventName = event.event_type; | ||
break; | ||
case "identify": | ||
type = "identify"; | ||
break; | ||
case "screen": | ||
type = "screen"; | ||
break; | ||
case "group": | ||
type = "group"; | ||
break; | ||
case "alias": | ||
type = "alias"; | ||
break; | ||
default: | ||
type = "track"; | ||
eventName = event.event_type; | ||
break; | ||
} | ||
const clientIds = Object.keys(event.ids || event.click_id || {}).length > 0 | ||
? { | ||
ga4: event.ids?.ga | ||
? { | ||
clientId: event.ids.ga, | ||
} | ||
: undefined, | ||
fbp: event.ids?.fbp, | ||
fbc: event.ids?.fbc, | ||
...event.click_id, | ||
} | ||
: undefined; | ||
const loc = event.location || {}; | ||
const geo = Object.keys(loc).length > 0 | ||
? { | ||
city: { | ||
name: loc.city, | ||
}, | ||
continent: { | ||
code: loc.continent, | ||
}, | ||
country: { | ||
code: loc.country, | ||
name: loc.country_name, | ||
}, | ||
location: { | ||
latitude: loc.latitude, | ||
longitude: loc.longitude, | ||
timezone: loc.timezone, | ||
}, | ||
region: { | ||
code: loc.region, | ||
}, | ||
postalCode: { | ||
code: loc.zip, | ||
}, | ||
provider: { | ||
as: { | ||
num: loc.autonomous_system_number, | ||
name: loc.autonomous_system_organization, | ||
}, | ||
isp: loc.isp, | ||
domain: loc.domain, | ||
}, | ||
} | ||
: undefined; | ||
const traits = {}; | ||
transfer(traits, event.user, ["id", "anonymous_id"]); | ||
const properties = {}; | ||
transfer(properties, event, [ | ||
TableNameParameter, | ||
"anon_ip", | ||
"api_key", | ||
"click_id", | ||
"doc_encoding", | ||
"doc_host", | ||
"doc_path", | ||
"doc_search", | ||
"eventn_ctx_event_id", | ||
"event_type", | ||
"local_tz_offset", | ||
"page_title", | ||
"referer", | ||
"screen_resolution", | ||
"source_ip", | ||
"url", | ||
"user", | ||
"location", | ||
"parsed_ua", | ||
"user_agent", | ||
"user_language", | ||
"utc_time", | ||
"_timestamp", | ||
"utm", | ||
"vp_size", | ||
]); | ||
if (type === "page") { | ||
properties.url = event.url; | ||
properties.title = event.page_title; | ||
properties.referrer = event.referer; | ||
properties.search = event.doc_search; | ||
properties.host = event.doc_host; | ||
properties.path = event.doc_path; | ||
properties.width = parseInt(event.vp_size?.split("x")[0]); | ||
properties.height = parseInt(event.vp_size?.split("x")[1]); | ||
} | ||
const screen = {}; | ||
const sr = event.screen_resolution?.split("x"); | ||
if (sr?.length === 2) { | ||
screen.width = parseInt(sr[0]); | ||
screen.height = parseInt(sr[1]); | ||
} | ||
const vs = event.vp_size?.split("x"); | ||
if (vs?.length === 2) { | ||
screen.innerWidth = parseInt(vs[0]); | ||
screen.innerHeight = parseInt(vs[1]); | ||
} | ||
return removeUndefined({ | ||
[TableNameParameter]: event[TableNameParameter], | ||
messageId: event.eventn_ctx_event_id, | ||
userId: event.user?.id, | ||
anonymousId: event.user?.anonymous_id, | ||
timestamp: event.utc_time, | ||
receivedAt: event._timestamp, | ||
writeKey: event.api_key, | ||
type, | ||
event: eventName, | ||
context: { | ||
ip: event.source_ip, | ||
locale: event.user_language, | ||
userAgent: event.user_agent, | ||
page: { | ||
url: event.url, | ||
title: event.page_title, | ||
referrer: event.referer, | ||
search: event.doc_search, | ||
host: event.doc_host, | ||
path: event.doc_path, | ||
encoding: event.doc_encoding, | ||
timezoneOffset: event.local_tz_offset, | ||
}, | ||
screen: Object.keys(screen).length > 0 ? screen : undefined, | ||
clientIds, | ||
campaign: event.utm, | ||
traits, | ||
geo, | ||
}, | ||
properties, | ||
traits: type === "identify" || type === "group" ? traits : undefined, | ||
}); | ||
} | ||
@@ -57,1 +490,14 @@ exports.DropRetryErrorName = DropRetryErrorName; | ||
exports.RetryErrorName = RetryErrorName; | ||
exports.TableNameParameter = TableNameParameter; | ||
exports.fromJitsuClassic = fromJitsuClassic; | ||
exports.idToClassic = idToClassic; | ||
exports.idToSnakeCaseFast = idToSnakeCaseFast; | ||
exports.removeUndefined = removeUndefined; | ||
exports.toClassic = toClassic; | ||
exports.toJitsuClassic = toJitsuClassic; | ||
exports.toSnakeCase = toSnakeCase; | ||
exports.transfer = transfer; | ||
exports.transferAsClassic = transferAsClassic; | ||
exports.transferAsSnakeCase = transferAsSnakeCase; | ||
exports.transferValue = transferValue; | ||
exports.transferValueAsSnakeCase = transferValueAsSnakeCase; |
export * from "./lib/functions"; | ||
export * from "./lib/objects"; | ||
export * from "./lib/strings"; |
@@ -0,1 +1,158 @@ | ||
const ACode = "A".charCodeAt(0); | ||
const ZCode = "Z".charCodeAt(0); | ||
const aCode = "a".charCodeAt(0); | ||
const zCode = "z".charCodeAt(0); | ||
const zeroCode = "0".charCodeAt(0); | ||
const nineCode = "9".charCodeAt(0); | ||
const spaceCode = " ".charCodeAt(0); | ||
const underscodeCode = "_".charCodeAt(0); | ||
function idToSnakeCaseFast(id) { | ||
let res = ""; | ||
let concatIndex = 0; | ||
let i = 0; | ||
let needUnderscore = false; | ||
for (; i < id.length; i++) { | ||
const c = id.charCodeAt(i); | ||
if (c >= ACode && c <= ZCode) { | ||
res += id.substring(concatIndex, i) + (needUnderscore ? "_" : "") + id.charAt(i).toLowerCase(); | ||
concatIndex = i + 1; | ||
} | ||
else if (c == spaceCode) { | ||
res += id.substring(concatIndex, i) + "_"; | ||
concatIndex = i + 1; | ||
} | ||
// needUnderscore is used in case next char is a capital latin letter | ||
// we add underscore only between latin letters | ||
needUnderscore = (c >= aCode && c <= zCode) || (c >= ACode && c <= ZCode); | ||
} | ||
if (concatIndex == 0) { | ||
return id; | ||
} | ||
else if (concatIndex < i) { | ||
res += id.substring(concatIndex, i); | ||
} | ||
return res; | ||
} | ||
function idToClassic(id) { | ||
let needConvert = false; | ||
for (let i = 0; i < id.length; i++) { | ||
const c = id.charCodeAt(i); | ||
if (!((c >= aCode && c <= zCode) || (c >= zeroCode && c <= nineCode) || c == underscodeCode)) { | ||
needConvert = true; | ||
break; | ||
} | ||
} | ||
if (!needConvert) { | ||
return id; | ||
} | ||
let res = ""; | ||
for (let i = 0; i < id.length; i++) { | ||
const c = id.charCodeAt(i); | ||
if (c >= ACode && c <= ZCode) { | ||
res += id.charAt(i).toLowerCase(); | ||
} | ||
else if ((c >= aCode && c <= zCode) || (c >= zeroCode && c <= nineCode)) { | ||
res += id.charAt(i); | ||
} | ||
else { | ||
res += "_"; | ||
} | ||
} | ||
return res; | ||
} | ||
function toSnakeCase(param) { | ||
if (Array.isArray(param)) { | ||
return param.map(toSnakeCase); | ||
} | ||
else if (typeof param === "object" && param !== null) { | ||
const r = {}; | ||
for (const [key, value] of Object.entries(param)) { | ||
r[idToSnakeCaseFast(key)] = toSnakeCase(value); | ||
} | ||
return r; | ||
} | ||
else { | ||
return param; | ||
} | ||
} | ||
function toClassic(param) { | ||
if (Array.isArray(param)) { | ||
return param.map(toClassic); | ||
} | ||
else if (typeof param === "object" && param !== null) { | ||
const r = {}; | ||
for (const [key, value] of Object.entries(param)) { | ||
r[idToClassic(key)] = toClassic(value); | ||
} | ||
return r; | ||
} | ||
else { | ||
return param; | ||
} | ||
} | ||
function removeUndefined(param) { | ||
if (Array.isArray(param)) { | ||
return param.map(removeUndefined); | ||
} | ||
else if (typeof param === "object" && param !== null) { | ||
for (const [key, value] of Object.entries(param)) { | ||
switch (typeof value) { | ||
case "undefined": | ||
delete param[key]; | ||
break; | ||
case "object": | ||
if (value !== null) { | ||
removeUndefined(value); | ||
} | ||
break; | ||
} | ||
} | ||
} | ||
return param; | ||
} | ||
function transferAsSnakeCase(target, source, omit) { | ||
if (typeof source !== "object") { | ||
return; | ||
} | ||
for (const [k, v] of Object.entries(source)) { | ||
if (!omit || !omit.includes(k)) { | ||
target[idToSnakeCaseFast(k)] = toSnakeCase(v); | ||
} | ||
} | ||
} | ||
function transferAsClassic(target, source, omit) { | ||
if (typeof source !== "object") { | ||
return; | ||
} | ||
for (const [k, v] of Object.entries(source)) { | ||
if (!omit || !omit.includes(k)) { | ||
target[idToClassic(k)] = toClassic(v); | ||
} | ||
} | ||
} | ||
function transferValueAsSnakeCase(target, property, source) { | ||
if (typeof source === "undefined") { | ||
return; | ||
} | ||
target[property] = toSnakeCase(source); | ||
} | ||
function transfer(target, source, omit) { | ||
if (typeof source !== "object") { | ||
return; | ||
} | ||
for (const [k, v] of Object.entries(source)) { | ||
if (!omit || !omit.includes(k)) { | ||
target[k] = v; | ||
} | ||
} | ||
} | ||
function transferValue(target, property, source) { | ||
if (typeof source === "undefined") { | ||
return; | ||
} | ||
target[property] = source; | ||
} | ||
const TableNameParameter = "JITSU_TABLE_NAME"; | ||
const DropRetryErrorName = "Drop & RetryError"; | ||
@@ -49,3 +206,279 @@ const RetryErrorName = "RetryError"; | ||
} | ||
function anonymizeIp(ip) { | ||
if (!ip) { | ||
return; | ||
} | ||
const parts = ip.split("."); | ||
if (parts.length === 4) { | ||
return `${parts[0]}.${parts[1]}.${parts[2]}.0`; | ||
} | ||
} | ||
function toJitsuClassic(event, ctx) { | ||
const keepOriginalNames = !!ctx.props.keepOriginalNames; | ||
const fileStorage = ctx.destination.type === "s3" || ctx.destination.type === "gcs"; | ||
let transferFunc = transferAsSnakeCase; | ||
if (keepOriginalNames) { | ||
if (fileStorage) { | ||
transferFunc = transfer; | ||
} | ||
else { | ||
transferFunc = transferAsClassic; | ||
} | ||
} | ||
let url = undefined; | ||
const analyticsContext = event.context || {}; | ||
const urlStr = analyticsContext.page?.url || event.properties?.url; | ||
try { | ||
if (urlStr) { | ||
url = new URL(urlStr); | ||
} | ||
} | ||
catch (e) { } | ||
const click_id = {}; | ||
transferFunc(click_id, analyticsContext.clientIds, ["ga4", "fbp", "fbc"]); | ||
let ids = {}; | ||
if (Object.keys(analyticsContext.clientIds || {}).length > 0) { | ||
ids = removeUndefined({ | ||
ga: analyticsContext.clientIds.ga4?.clientId, | ||
fbp: analyticsContext.clientIds.fbp, | ||
fbc: analyticsContext.clientIds.fbc, | ||
}); | ||
} | ||
const geo = analyticsContext.geo || {}; | ||
const ua = ctx?.ua || {}; | ||
const user = removeUndefined({ | ||
id: event.userId, | ||
anonymous_id: event.anonymousId, | ||
email: (analyticsContext.traits?.email || event.traits?.email || undefined), | ||
name: (analyticsContext.traits?.name || event.traits?.name || undefined), | ||
}); | ||
transferFunc(user, analyticsContext.traits, ["email", "name"]); | ||
transferFunc(user, event.traits, ["email", "name"]); | ||
const classic = { | ||
[TableNameParameter]: event[TableNameParameter], | ||
anon_ip: analyticsContext.ip ? anonymizeIp(analyticsContext.ip) : undefined, | ||
api_key: event.writeKey || "", | ||
click_id: Object.keys(click_id).length > 0 ? click_id : undefined, | ||
doc_encoding: analyticsContext.page?.encoding ?? event.properties?.encoding, | ||
doc_host: (analyticsContext.page?.host ?? event.properties?.host) || url?.host, | ||
doc_path: (analyticsContext.page?.path ?? event.properties?.path) || url?.pathname, | ||
doc_search: (analyticsContext.page?.search ?? event.properties?.search) || url?.search, | ||
eventn_ctx_event_id: event.messageId, | ||
event_type: event.event || event.type, | ||
local_tz_offset: analyticsContext.page?.timezoneOffset ?? event.properties?.timezoneOffset, | ||
page_title: analyticsContext.page?.title, | ||
referer: analyticsContext.page?.referrer, | ||
screen_resolution: Object.keys(analyticsContext.screen || {}).length > 0 | ||
? Math.max(analyticsContext.screen.width || 0) + "x" + Math.max(analyticsContext.screen.height || 0) | ||
: undefined, | ||
source_ip: analyticsContext.ip, | ||
src: event.properties?.src || "jitsu", | ||
url: urlStr, | ||
user: Object.keys(user).length > 0 ? user : undefined, | ||
location: Object.keys(geo).length > 0 | ||
? { | ||
city: geo.city?.name, | ||
continent: geo.continent?.name, | ||
country: geo.country?.code, | ||
country_name: geo.country?.name, | ||
latitude: geo.location?.latitude, | ||
longitude: geo.location?.longitude, | ||
region: geo.region?.code, | ||
zip: geo.postalCode?.code, | ||
timezone: geo.location?.timezone, | ||
autonomous_system_number: geo.provider?.as?.num, | ||
autonomous_system_organization: geo.provider?.as?.name, | ||
isp: geo.provider?.isp, | ||
domain: geo.provider?.domain, | ||
} | ||
: undefined, | ||
ids: Object.keys(ids).length > 0 ? ids : undefined, | ||
parsed_ua: Object.keys(ua).length > 0 | ||
? { | ||
os_family: ua.os?.name, | ||
os_version: ua.os?.version, | ||
ua_family: ua.browser?.name, | ||
ua_version: ua.browser?.version, | ||
device_brand: ua.device?.vendor, | ||
device_type: ua.device?.type, | ||
device_model: ua.device?.model, | ||
bot: ua.bot, | ||
} | ||
: undefined, | ||
user_agent: analyticsContext.userAgent, | ||
user_language: analyticsContext.locale, | ||
utc_time: event.timestamp, | ||
_timestamp: event.receivedAt, | ||
utm: analyticsContext.campaign, | ||
vp_size: Object.keys(analyticsContext.screen || {}).length > 0 | ||
? Math.max(analyticsContext.screen.innerWidth || 0) + "x" + Math.max(analyticsContext.screen.innerHeight || 0) | ||
: undefined, | ||
}; | ||
if (event.type === "track") { | ||
transferFunc(classic, event.properties); | ||
} | ||
else { | ||
transferFunc(classic, event.properties, ["url", "title", "referrer", "search", "host", "path", "width", "height"]); | ||
} | ||
return removeUndefined(classic); | ||
} | ||
function fromJitsuClassic(event) { | ||
let type = "track"; | ||
let eventName = undefined; | ||
switch ((event.event_type ?? "").toLowerCase()) { | ||
case "pageview": | ||
case "page_view": | ||
case "page": | ||
type = "page"; | ||
eventName = event.event_type; | ||
break; | ||
case "identify": | ||
type = "identify"; | ||
break; | ||
case "screen": | ||
type = "screen"; | ||
break; | ||
case "group": | ||
type = "group"; | ||
break; | ||
case "alias": | ||
type = "alias"; | ||
break; | ||
default: | ||
type = "track"; | ||
eventName = event.event_type; | ||
break; | ||
} | ||
const clientIds = Object.keys(event.ids || event.click_id || {}).length > 0 | ||
? { | ||
ga4: event.ids?.ga | ||
? { | ||
clientId: event.ids.ga, | ||
} | ||
: undefined, | ||
fbp: event.ids?.fbp, | ||
fbc: event.ids?.fbc, | ||
...event.click_id, | ||
} | ||
: undefined; | ||
const loc = event.location || {}; | ||
const geo = Object.keys(loc).length > 0 | ||
? { | ||
city: { | ||
name: loc.city, | ||
}, | ||
continent: { | ||
code: loc.continent, | ||
}, | ||
country: { | ||
code: loc.country, | ||
name: loc.country_name, | ||
}, | ||
location: { | ||
latitude: loc.latitude, | ||
longitude: loc.longitude, | ||
timezone: loc.timezone, | ||
}, | ||
region: { | ||
code: loc.region, | ||
}, | ||
postalCode: { | ||
code: loc.zip, | ||
}, | ||
provider: { | ||
as: { | ||
num: loc.autonomous_system_number, | ||
name: loc.autonomous_system_organization, | ||
}, | ||
isp: loc.isp, | ||
domain: loc.domain, | ||
}, | ||
} | ||
: undefined; | ||
const traits = {}; | ||
transfer(traits, event.user, ["id", "anonymous_id"]); | ||
const properties = {}; | ||
transfer(properties, event, [ | ||
TableNameParameter, | ||
"anon_ip", | ||
"api_key", | ||
"click_id", | ||
"doc_encoding", | ||
"doc_host", | ||
"doc_path", | ||
"doc_search", | ||
"eventn_ctx_event_id", | ||
"event_type", | ||
"local_tz_offset", | ||
"page_title", | ||
"referer", | ||
"screen_resolution", | ||
"source_ip", | ||
"url", | ||
"user", | ||
"location", | ||
"parsed_ua", | ||
"user_agent", | ||
"user_language", | ||
"utc_time", | ||
"_timestamp", | ||
"utm", | ||
"vp_size", | ||
]); | ||
if (type === "page") { | ||
properties.url = event.url; | ||
properties.title = event.page_title; | ||
properties.referrer = event.referer; | ||
properties.search = event.doc_search; | ||
properties.host = event.doc_host; | ||
properties.path = event.doc_path; | ||
properties.width = parseInt(event.vp_size?.split("x")[0]); | ||
properties.height = parseInt(event.vp_size?.split("x")[1]); | ||
} | ||
const screen = {}; | ||
const sr = event.screen_resolution?.split("x"); | ||
if (sr?.length === 2) { | ||
screen.width = parseInt(sr[0]); | ||
screen.height = parseInt(sr[1]); | ||
} | ||
const vs = event.vp_size?.split("x"); | ||
if (vs?.length === 2) { | ||
screen.innerWidth = parseInt(vs[0]); | ||
screen.innerHeight = parseInt(vs[1]); | ||
} | ||
return removeUndefined({ | ||
[TableNameParameter]: event[TableNameParameter], | ||
messageId: event.eventn_ctx_event_id, | ||
userId: event.user?.id, | ||
anonymousId: event.user?.anonymous_id, | ||
timestamp: event.utc_time, | ||
receivedAt: event._timestamp, | ||
writeKey: event.api_key, | ||
type, | ||
event: eventName, | ||
context: { | ||
ip: event.source_ip, | ||
locale: event.user_language, | ||
userAgent: event.user_agent, | ||
page: { | ||
url: event.url, | ||
title: event.page_title, | ||
referrer: event.referer, | ||
search: event.doc_search, | ||
host: event.doc_host, | ||
path: event.doc_path, | ||
encoding: event.doc_encoding, | ||
timezoneOffset: event.local_tz_offset, | ||
}, | ||
screen: Object.keys(screen).length > 0 ? screen : undefined, | ||
clientIds, | ||
campaign: event.utm, | ||
traits, | ||
geo, | ||
}, | ||
properties, | ||
traits: type === "identify" || type === "group" ? traits : undefined, | ||
}); | ||
} | ||
export { DropRetryErrorName, HTTPError, RetryError, RetryErrorName }; | ||
export { DropRetryErrorName, HTTPError, RetryError, RetryErrorName, TableNameParameter, fromJitsuClassic, idToClassic, idToSnakeCaseFast, removeUndefined, toClassic, toJitsuClassic, toSnakeCase, transfer, transferAsClassic, transferAsSnakeCase, transferValue, transferValueAsSnakeCase }; |
@@ -0,1 +1,4 @@ | ||
import { AnyEvent, FullContext } from "@jitsu/protocols/functions"; | ||
import { AnalyticsServerEvent } from "@jitsu/protocols/analytics"; | ||
export declare const TableNameParameter = "JITSU_TABLE_NAME"; | ||
export declare const DropRetryErrorName = "Drop & RetryError"; | ||
@@ -29,1 +32,3 @@ export declare const RetryErrorName = "RetryError"; | ||
} | ||
export declare function toJitsuClassic(event: AnalyticsServerEvent, ctx: FullContext): AnyEvent; | ||
export declare function fromJitsuClassic(event: AnyEvent): AnyEvent; |
export declare function toSnakeCase(param: any): any; | ||
export declare function toClassic(param: any): any; | ||
export declare function removeUndefined(param: any): any; | ||
export declare function transferAsSnakeCase(target: Record<string, any>, source: any, omit?: string[]): void; | ||
export declare function transferAsClassic(target: Record<string, any>, source: any, omit?: string[]): void; | ||
export declare function transferValueAsSnakeCase(target: Record<string, any>, property: string, source: any): void; | ||
export declare function transfer(target: Record<string, any>, source: any, omit?: string[]): void; | ||
export declare function transferValue(target: Record<string, any>, property: string, source: any): void; |
export declare function idToSnakeCaseFast(id: string): string; | ||
export declare function idToClassic(id: string): string; |
{ | ||
"name": "@jitsu/functions-lib", | ||
"version": "1.9.12", | ||
"version": "1.9.13-canary.1167.20250215132227", | ||
"main": "dist/index.cjs.js", | ||
@@ -17,15 +17,17 @@ "module": "dist/index.es.js", | ||
"@rollup/plugin-typescript": "^11.1.5", | ||
"@rollup/plugin-node-resolve": "^16.0.0", | ||
"@rollup/plugin-commonjs": "^28.0.2", | ||
"@types/jest": "^29.1.1", | ||
"@types/node": "^18.15.3", | ||
"jest": "^29.1.2", | ||
"ts-jest": "29.0.5" | ||
"ts-jest": "29.0.5", | ||
"tslib": "^2.6.3", | ||
"@jitsu/sdk-js": "^3.1.5", | ||
"@jitsu/protocols": "1.9.13-canary.1167.20250215132227" | ||
}, | ||
"dependencies": { | ||
"lodash": "^4.17.21", | ||
"tslib": "^2.6.3" | ||
}, | ||
"scripts": { | ||
"compile": "tsc -p .", | ||
"build": "pnpm compile && rollup -c" | ||
"build": "pnpm compile && rollup -c", | ||
"test": "tsc -p . && jest --verbose" | ||
} | ||
} |
const typescript = require("@rollup/plugin-typescript"); | ||
const resolve = require("@rollup/plugin-node-resolve"); | ||
const commonjs = require("@rollup/plugin-commonjs"); | ||
module.exports = [ | ||
{ | ||
plugins: [typescript()], | ||
plugins: [typescript(), resolve({ preferBuiltins: false }), commonjs()], | ||
input: ["./src/index.ts"], | ||
@@ -7,0 +9,0 @@ output: [ |
export * from "./lib/functions"; | ||
export * from "./lib/objects"; | ||
export * from "./lib/strings"; |
@@ -0,1 +1,7 @@ | ||
import { AnyEvent, FullContext, UserAgent } from "@jitsu/protocols/functions"; | ||
import { AnalyticsServerEvent } from "@jitsu/protocols/analytics"; | ||
import { removeUndefined, transferAsSnakeCase, transfer, transferAsClassic } from "./objects"; | ||
export const TableNameParameter = "JITSU_TABLE_NAME"; | ||
export const DropRetryErrorName = "Drop & RetryError"; | ||
@@ -51,1 +57,285 @@ export const RetryErrorName = "RetryError"; | ||
} | ||
function anonymizeIp(ip: string | undefined) { | ||
if (!ip) { | ||
return; | ||
} | ||
const parts = ip.split("."); | ||
if (parts.length === 4) { | ||
return `${parts[0]}.${parts[1]}.${parts[2]}.0`; | ||
} | ||
} | ||
export function toJitsuClassic(event: AnalyticsServerEvent, ctx: FullContext): AnyEvent { | ||
const keepOriginalNames = !!ctx.props.keepOriginalNames; | ||
const fileStorage = ctx.destination.type === "s3" || ctx.destination.type === "gcs"; | ||
let transferFunc = transferAsSnakeCase; | ||
if (keepOriginalNames) { | ||
if (fileStorage) { | ||
transferFunc = transfer; | ||
} else { | ||
transferFunc = transferAsClassic; | ||
} | ||
} | ||
let url: URL | undefined = undefined; | ||
const analyticsContext = event.context || {}; | ||
const urlStr = analyticsContext.page?.url || event.properties?.url; | ||
try { | ||
if (urlStr) { | ||
url = new URL(urlStr as string); | ||
} | ||
} catch (e) {} | ||
const click_id = {}; | ||
transferFunc(click_id, analyticsContext.clientIds, ["ga4", "fbp", "fbc"]); | ||
let ids: any = {}; | ||
if (Object.keys(analyticsContext.clientIds || {}).length > 0) { | ||
ids = removeUndefined({ | ||
ga: analyticsContext.clientIds.ga4?.clientId, | ||
fbp: analyticsContext.clientIds.fbp, | ||
fbc: analyticsContext.clientIds.fbc, | ||
}); | ||
} | ||
const geo = analyticsContext.geo || {}; | ||
const ua: UserAgent = ctx?.ua || ({} as UserAgent); | ||
const user = removeUndefined({ | ||
id: event.userId, | ||
anonymous_id: event.anonymousId, | ||
email: (analyticsContext.traits?.email || event.traits?.email || undefined) as string | undefined, | ||
name: (analyticsContext.traits?.name || event.traits?.name || undefined) as string | undefined, | ||
}); | ||
transferFunc(user, analyticsContext.traits, ["email", "name"]); | ||
transferFunc(user, event.traits, ["email", "name"]); | ||
const classic = { | ||
[TableNameParameter]: event[TableNameParameter], | ||
anon_ip: analyticsContext.ip ? anonymizeIp(analyticsContext.ip) : undefined, | ||
api_key: event.writeKey || "", | ||
click_id: Object.keys(click_id).length > 0 ? click_id : undefined, | ||
doc_encoding: analyticsContext.page?.encoding ?? event.properties?.encoding, | ||
doc_host: (analyticsContext.page?.host ?? event.properties?.host) || url?.host, | ||
doc_path: (analyticsContext.page?.path ?? event.properties?.path) || url?.pathname, | ||
doc_search: (analyticsContext.page?.search ?? event.properties?.search) || url?.search, | ||
eventn_ctx_event_id: event.messageId, | ||
event_type: event.event || event.type, | ||
local_tz_offset: analyticsContext.page?.timezoneOffset ?? event.properties?.timezoneOffset, | ||
page_title: analyticsContext.page?.title, | ||
referer: analyticsContext.page?.referrer, | ||
screen_resolution: | ||
Object.keys(analyticsContext.screen || {}).length > 0 | ||
? Math.max(analyticsContext.screen.width || 0) + "x" + Math.max(analyticsContext.screen.height || 0) | ||
: undefined, | ||
source_ip: analyticsContext.ip, | ||
src: event.properties?.src || "jitsu", | ||
url: urlStr as string, | ||
user: Object.keys(user).length > 0 ? user : undefined, | ||
location: | ||
Object.keys(geo).length > 0 | ||
? { | ||
city: geo.city?.name, | ||
continent: geo.continent?.name, | ||
country: geo.country?.code, | ||
country_name: geo.country?.name, | ||
latitude: geo.location?.latitude, | ||
longitude: geo.location?.longitude, | ||
region: geo.region?.code, | ||
zip: geo.postalCode?.code, | ||
timezone: geo.location?.timezone, | ||
autonomous_system_number: geo.provider?.as?.num, | ||
autonomous_system_organization: geo.provider?.as?.name, | ||
isp: geo.provider?.isp, | ||
domain: geo.provider?.domain, | ||
} | ||
: undefined, | ||
ids: Object.keys(ids).length > 0 ? ids : undefined, | ||
parsed_ua: | ||
Object.keys(ua).length > 0 | ||
? { | ||
os_family: ua.os?.name, | ||
os_version: ua.os?.version, | ||
ua_family: ua.browser?.name, | ||
ua_version: ua.browser?.version, | ||
device_brand: ua.device?.vendor, | ||
device_type: ua.device?.type, | ||
device_model: ua.device?.model, | ||
bot: ua.bot, | ||
} | ||
: undefined, | ||
user_agent: analyticsContext.userAgent, | ||
user_language: analyticsContext.locale, | ||
utc_time: event.timestamp, | ||
_timestamp: event.receivedAt, | ||
utm: analyticsContext.campaign, | ||
vp_size: | ||
Object.keys(analyticsContext.screen || {}).length > 0 | ||
? Math.max(analyticsContext.screen.innerWidth || 0) + "x" + Math.max(analyticsContext.screen.innerHeight || 0) | ||
: undefined, | ||
}; | ||
if (event.type === "track") { | ||
transferFunc(classic, event.properties); | ||
} else { | ||
transferFunc(classic, event.properties, ["url", "title", "referrer", "search", "host", "path", "width", "height"]); | ||
} | ||
return removeUndefined(classic); | ||
} | ||
export function fromJitsuClassic(event: AnyEvent): AnyEvent { | ||
let type = "track"; | ||
let eventName: string | undefined = undefined; | ||
switch ((event.event_type ?? "").toLowerCase()) { | ||
case "pageview": | ||
case "page_view": | ||
case "page": | ||
type = "page"; | ||
eventName = event.event_type; | ||
break; | ||
case "identify": | ||
type = "identify"; | ||
break; | ||
case "screen": | ||
type = "screen"; | ||
break; | ||
case "group": | ||
type = "group"; | ||
break; | ||
case "alias": | ||
type = "alias"; | ||
break; | ||
default: | ||
type = "track"; | ||
eventName = event.event_type; | ||
break; | ||
} | ||
const clientIds = | ||
Object.keys(event.ids || event.click_id || {}).length > 0 | ||
? { | ||
ga4: event.ids?.ga | ||
? { | ||
clientId: event.ids.ga, | ||
} | ||
: undefined, | ||
fbp: event.ids?.fbp, | ||
fbc: event.ids?.fbc, | ||
...event.click_id, | ||
} | ||
: undefined; | ||
const loc = event.location || {}; | ||
const geo = | ||
Object.keys(loc).length > 0 | ||
? { | ||
city: { | ||
name: loc.city, | ||
}, | ||
continent: { | ||
code: loc.continent, | ||
}, | ||
country: { | ||
code: loc.country, | ||
name: loc.country_name, | ||
}, | ||
location: { | ||
latitude: loc.latitude, | ||
longitude: loc.longitude, | ||
timezone: loc.timezone, | ||
}, | ||
region: { | ||
code: loc.region, | ||
}, | ||
postalCode: { | ||
code: loc.zip, | ||
}, | ||
provider: { | ||
as: { | ||
num: loc.autonomous_system_number, | ||
name: loc.autonomous_system_organization, | ||
}, | ||
isp: loc.isp, | ||
domain: loc.domain, | ||
}, | ||
} | ||
: undefined; | ||
const traits = {}; | ||
transfer(traits, event.user, ["id", "anonymous_id"]); | ||
const properties: any = {}; | ||
transfer(properties, event, [ | ||
TableNameParameter, | ||
"anon_ip", | ||
"api_key", | ||
"click_id", | ||
"doc_encoding", | ||
"doc_host", | ||
"doc_path", | ||
"doc_search", | ||
"eventn_ctx_event_id", | ||
"event_type", | ||
"local_tz_offset", | ||
"page_title", | ||
"referer", | ||
"screen_resolution", | ||
"source_ip", | ||
"url", | ||
"user", | ||
"location", | ||
"parsed_ua", | ||
"user_agent", | ||
"user_language", | ||
"utc_time", | ||
"_timestamp", | ||
"utm", | ||
"vp_size", | ||
]); | ||
if (type === "page") { | ||
properties.url = event.url; | ||
properties.title = event.page_title; | ||
properties.referrer = event.referer; | ||
properties.search = event.doc_search; | ||
properties.host = event.doc_host; | ||
properties.path = event.doc_path; | ||
properties.width = parseInt(event.vp_size?.split("x")[0]); | ||
properties.height = parseInt(event.vp_size?.split("x")[1]); | ||
} | ||
const screen: any = {}; | ||
const sr = event.screen_resolution?.split("x"); | ||
if (sr?.length === 2) { | ||
screen.width = parseInt(sr[0]); | ||
screen.height = parseInt(sr[1]); | ||
} | ||
const vs = event.vp_size?.split("x"); | ||
if (vs?.length === 2) { | ||
screen.innerWidth = parseInt(vs[0]); | ||
screen.innerHeight = parseInt(vs[1]); | ||
} | ||
return removeUndefined({ | ||
[TableNameParameter]: event[TableNameParameter], | ||
messageId: event.eventn_ctx_event_id, | ||
userId: event.user?.id, | ||
anonymousId: event.user?.anonymous_id, | ||
timestamp: event.utc_time, | ||
receivedAt: event._timestamp, | ||
writeKey: event.api_key, | ||
type, | ||
event: eventName, | ||
context: { | ||
ip: event.source_ip, | ||
locale: event.user_language, | ||
userAgent: event.user_agent, | ||
page: { | ||
url: event.url, | ||
title: event.page_title, | ||
referrer: event.referer, | ||
search: event.doc_search, | ||
host: event.doc_host, | ||
path: event.doc_path, | ||
encoding: event.doc_encoding, | ||
timezoneOffset: event.local_tz_offset, | ||
}, | ||
screen: Object.keys(screen).length > 0 ? screen : undefined, | ||
clientIds, | ||
campaign: event.utm, | ||
traits, | ||
geo, | ||
}, | ||
properties, | ||
traits: type === "identify" || type === "group" ? traits : undefined, | ||
}); | ||
} |
@@ -8,2 +8,3 @@ { | ||
"esModuleInterop": true, | ||
"moduleResolution": "node", | ||
"noEmit": true, | ||
@@ -21,2 +22,2 @@ "target": "esnext", | ||
] | ||
} | ||
} |
Sorry, the diff of this file is not supported yet
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
186235
45.68%0
-100%20
42.86%3262
124.35%0
-100%11
83.33%2
100%- Removed
- Removed
- Removed
- Removed