@jitsu/functions-lib 1.9.12 → 1.9.13

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/dist/index.es.js CHANGED
@@ -1,3 +1,160 @@
1
+ const ACode = "A".charCodeAt(0);
2
+ const ZCode = "Z".charCodeAt(0);
3
+ const aCode = "a".charCodeAt(0);
4
+ const zCode = "z".charCodeAt(0);
5
+ const zeroCode = "0".charCodeAt(0);
6
+ const nineCode = "9".charCodeAt(0);
7
+ const spaceCode = " ".charCodeAt(0);
8
+ const underscodeCode = "_".charCodeAt(0);
9
+ function idToSnakeCaseFast(id) {
10
+ let res = "";
11
+ let concatIndex = 0;
12
+ let i = 0;
13
+ let needUnderscore = false;
14
+ for (; i < id.length; i++) {
15
+ const c = id.charCodeAt(i);
16
+ if (c >= ACode && c <= ZCode) {
17
+ res += id.substring(concatIndex, i) + (needUnderscore ? "_" : "") + id.charAt(i).toLowerCase();
18
+ concatIndex = i + 1;
19
+ }
20
+ else if (c == spaceCode) {
21
+ res += id.substring(concatIndex, i) + "_";
22
+ concatIndex = i + 1;
23
+ }
24
+ // needUnderscore is used in case next char is a capital latin letter
25
+ // we add underscore only between latin letters
26
+ needUnderscore = (c >= aCode && c <= zCode) || (c >= ACode && c <= ZCode);
27
+ }
28
+ if (concatIndex == 0) {
29
+ return id;
30
+ }
31
+ else if (concatIndex < i) {
32
+ res += id.substring(concatIndex, i);
33
+ }
34
+ return res;
35
+ }
36
+ function idToClassic(id) {
37
+ let needConvert = false;
38
+ for (let i = 0; i < id.length; i++) {
39
+ const c = id.charCodeAt(i);
40
+ if (!((c >= aCode && c <= zCode) || (c >= zeroCode && c <= nineCode) || c == underscodeCode)) {
41
+ needConvert = true;
42
+ break;
43
+ }
44
+ }
45
+ if (!needConvert) {
46
+ return id;
47
+ }
48
+ let res = "";
49
+ for (let i = 0; i < id.length; i++) {
50
+ const c = id.charCodeAt(i);
51
+ if (c >= ACode && c <= ZCode) {
52
+ res += id.charAt(i).toLowerCase();
53
+ }
54
+ else if ((c >= aCode && c <= zCode) || (c >= zeroCode && c <= nineCode)) {
55
+ res += id.charAt(i);
56
+ }
57
+ else {
58
+ res += "_";
59
+ }
60
+ }
61
+ return res;
62
+ }
63
+
64
+ function toSnakeCase(param) {
65
+ if (Array.isArray(param)) {
66
+ return param.map(toSnakeCase);
67
+ }
68
+ else if (typeof param === "object" && param !== null) {
69
+ const r = {};
70
+ for (const [key, value] of Object.entries(param)) {
71
+ r[idToSnakeCaseFast(key)] = toSnakeCase(value);
72
+ }
73
+ return r;
74
+ }
75
+ else {
76
+ return param;
77
+ }
78
+ }
79
+ function toClassic(param) {
80
+ if (Array.isArray(param)) {
81
+ return param.map(toClassic);
82
+ }
83
+ else if (typeof param === "object" && param !== null) {
84
+ const r = {};
85
+ for (const [key, value] of Object.entries(param)) {
86
+ r[idToClassic(key)] = toClassic(value);
87
+ }
88
+ return r;
89
+ }
90
+ else {
91
+ return param;
92
+ }
93
+ }
94
+ function removeUndefined(param) {
95
+ if (Array.isArray(param)) {
96
+ return param.map(removeUndefined);
97
+ }
98
+ else if (typeof param === "object" && param !== null) {
99
+ for (const [key, value] of Object.entries(param)) {
100
+ switch (typeof value) {
101
+ case "undefined":
102
+ delete param[key];
103
+ break;
104
+ case "object":
105
+ if (value !== null) {
106
+ removeUndefined(value);
107
+ }
108
+ break;
109
+ }
110
+ }
111
+ }
112
+ return param;
113
+ }
114
+ function transferAsSnakeCase(target, source, omit) {
115
+ if (typeof source !== "object") {
116
+ return;
117
+ }
118
+ for (const [k, v] of Object.entries(source)) {
119
+ if (!omit || !omit.includes(k)) {
120
+ target[idToSnakeCaseFast(k)] = toSnakeCase(v);
121
+ }
122
+ }
123
+ }
124
+ function transferAsClassic(target, source, omit) {
125
+ if (typeof source !== "object") {
126
+ return;
127
+ }
128
+ for (const [k, v] of Object.entries(source)) {
129
+ if (!omit || !omit.includes(k)) {
130
+ target[idToClassic(k)] = toClassic(v);
131
+ }
132
+ }
133
+ }
134
+ function transferValueAsSnakeCase(target, property, source) {
135
+ if (typeof source === "undefined") {
136
+ return;
137
+ }
138
+ target[property] = toSnakeCase(source);
139
+ }
140
+ function transfer(target, source, omit) {
141
+ if (typeof source !== "object") {
142
+ return;
143
+ }
144
+ for (const [k, v] of Object.entries(source)) {
145
+ if (!omit || !omit.includes(k)) {
146
+ target[k] = v;
147
+ }
148
+ }
149
+ }
150
+ function transferValue(target, property, source) {
151
+ if (typeof source === "undefined") {
152
+ return;
153
+ }
154
+ target[property] = source;
155
+ }
156
+
157
+ const TableNameParameter = "JITSU_TABLE_NAME";
1
158
  const DropRetryErrorName = "Drop & RetryError";
2
159
  const RetryErrorName = "RetryError";
3
160
  class RetryError extends Error {
@@ -46,5 +203,281 @@ class HTTPError extends Error {
46
203
  };
47
204
  }
48
205
  }
206
+ function anonymizeIp(ip) {
207
+ if (!ip) {
208
+ return;
209
+ }
210
+ const parts = ip.split(".");
211
+ if (parts.length === 4) {
212
+ return `${parts[0]}.${parts[1]}.${parts[2]}.0`;
213
+ }
214
+ }
215
+ function toJitsuClassic(event, ctx) {
216
+ const keepOriginalNames = !!ctx.props.keepOriginalNames;
217
+ const fileStorage = ctx.destination.type === "s3" || ctx.destination.type === "gcs";
218
+ let transferFunc = transferAsSnakeCase;
219
+ if (keepOriginalNames) {
220
+ if (fileStorage) {
221
+ transferFunc = transfer;
222
+ }
223
+ else {
224
+ transferFunc = transferAsClassic;
225
+ }
226
+ }
227
+ let url = undefined;
228
+ const analyticsContext = event.context || {};
229
+ const urlStr = analyticsContext.page?.url || event.properties?.url;
230
+ try {
231
+ if (urlStr) {
232
+ url = new URL(urlStr);
233
+ }
234
+ }
235
+ catch (e) { }
236
+ const click_id = {};
237
+ transferFunc(click_id, analyticsContext.clientIds, ["ga4", "fbp", "fbc"]);
238
+ let ids = {};
239
+ if (Object.keys(analyticsContext.clientIds || {}).length > 0) {
240
+ ids = removeUndefined({
241
+ ga: analyticsContext.clientIds.ga4?.clientId,
242
+ fbp: analyticsContext.clientIds.fbp,
243
+ fbc: analyticsContext.clientIds.fbc,
244
+ });
245
+ }
246
+ const geo = analyticsContext.geo || {};
247
+ const ua = ctx?.ua || {};
248
+ const user = removeUndefined({
249
+ id: event.userId,
250
+ anonymous_id: event.anonymousId,
251
+ email: (analyticsContext.traits?.email || event.traits?.email || undefined),
252
+ name: (analyticsContext.traits?.name || event.traits?.name || undefined),
253
+ });
254
+ transferFunc(user, analyticsContext.traits, ["email", "name"]);
255
+ transferFunc(user, event.traits, ["email", "name"]);
256
+ const classic = {
257
+ [TableNameParameter]: event[TableNameParameter],
258
+ anon_ip: analyticsContext.ip ? anonymizeIp(analyticsContext.ip) : undefined,
259
+ api_key: event.writeKey || "",
260
+ click_id: Object.keys(click_id).length > 0 ? click_id : undefined,
261
+ doc_encoding: analyticsContext.page?.encoding ?? event.properties?.encoding,
262
+ doc_host: (analyticsContext.page?.host ?? event.properties?.host) || url?.host,
263
+ doc_path: (analyticsContext.page?.path ?? event.properties?.path) || url?.pathname,
264
+ doc_search: (analyticsContext.page?.search ?? event.properties?.search) || url?.search,
265
+ eventn_ctx_event_id: event.messageId,
266
+ event_type: event.event || event.type,
267
+ local_tz_offset: analyticsContext.page?.timezoneOffset ?? event.properties?.timezoneOffset,
268
+ page_title: analyticsContext.page?.title,
269
+ referer: analyticsContext.page?.referrer,
270
+ screen_resolution: Object.keys(analyticsContext.screen || {}).length > 0
271
+ ? Math.max(analyticsContext.screen.width || 0) + "x" + Math.max(analyticsContext.screen.height || 0)
272
+ : undefined,
273
+ source_ip: analyticsContext.ip,
274
+ src: event.properties?.src || "jitsu",
275
+ url: urlStr,
276
+ user: Object.keys(user).length > 0 ? user : undefined,
277
+ location: Object.keys(geo).length > 0
278
+ ? {
279
+ city: geo.city?.name,
280
+ continent: geo.continent?.name,
281
+ country: geo.country?.code,
282
+ country_name: geo.country?.name,
283
+ latitude: geo.location?.latitude,
284
+ longitude: geo.location?.longitude,
285
+ region: geo.region?.code,
286
+ zip: geo.postalCode?.code,
287
+ timezone: geo.location?.timezone,
288
+ autonomous_system_number: geo.provider?.as?.num,
289
+ autonomous_system_organization: geo.provider?.as?.name,
290
+ isp: geo.provider?.isp,
291
+ domain: geo.provider?.domain,
292
+ }
293
+ : undefined,
294
+ ids: Object.keys(ids).length > 0 ? ids : undefined,
295
+ parsed_ua: Object.keys(ua).length > 0
296
+ ? {
297
+ os_family: ua.os?.name,
298
+ os_version: ua.os?.version,
299
+ ua_family: ua.browser?.name,
300
+ ua_version: ua.browser?.version,
301
+ device_brand: ua.device?.vendor,
302
+ device_type: ua.device?.type,
303
+ device_model: ua.device?.model,
304
+ bot: ua.bot,
305
+ }
306
+ : undefined,
307
+ user_agent: analyticsContext.userAgent,
308
+ user_language: analyticsContext.locale,
309
+ utc_time: event.timestamp,
310
+ _timestamp: event.receivedAt,
311
+ utm: analyticsContext.campaign,
312
+ vp_size: Object.keys(analyticsContext.screen || {}).length > 0
313
+ ? Math.max(analyticsContext.screen.innerWidth || 0) + "x" + Math.max(analyticsContext.screen.innerHeight || 0)
314
+ : undefined,
315
+ };
316
+ if (event.type === "track") {
317
+ transferFunc(classic, event.properties);
318
+ }
319
+ else {
320
+ transferFunc(classic, event.properties, ["url", "title", "referrer", "search", "host", "path", "width", "height"]);
321
+ }
322
+ return removeUndefined(classic);
323
+ }
324
+ function fromJitsuClassic(event) {
325
+ let type = "track";
326
+ let eventName = undefined;
327
+ switch ((event.event_type ?? "").toLowerCase()) {
328
+ case "pageview":
329
+ case "page_view":
330
+ case "page":
331
+ type = "page";
332
+ eventName = event.event_type;
333
+ break;
334
+ case "identify":
335
+ type = "identify";
336
+ break;
337
+ case "screen":
338
+ type = "screen";
339
+ break;
340
+ case "group":
341
+ type = "group";
342
+ break;
343
+ case "alias":
344
+ type = "alias";
345
+ break;
346
+ default:
347
+ type = "track";
348
+ eventName = event.event_type;
349
+ break;
350
+ }
351
+ const clientIds = Object.keys(event.ids || event.click_id || {}).length > 0
352
+ ? {
353
+ ga4: event.ids?.ga
354
+ ? {
355
+ clientId: event.ids.ga,
356
+ }
357
+ : undefined,
358
+ fbp: event.ids?.fbp,
359
+ fbc: event.ids?.fbc,
360
+ ...event.click_id,
361
+ }
362
+ : undefined;
363
+ const loc = event.location || {};
364
+ const geo = Object.keys(loc).length > 0
365
+ ? {
366
+ city: {
367
+ name: loc.city,
368
+ },
369
+ continent: {
370
+ code: loc.continent,
371
+ },
372
+ country: {
373
+ code: loc.country,
374
+ name: loc.country_name,
375
+ },
376
+ location: {
377
+ latitude: loc.latitude,
378
+ longitude: loc.longitude,
379
+ timezone: loc.timezone,
380
+ },
381
+ region: {
382
+ code: loc.region,
383
+ },
384
+ postalCode: {
385
+ code: loc.zip,
386
+ },
387
+ provider: {
388
+ as: {
389
+ num: loc.autonomous_system_number,
390
+ name: loc.autonomous_system_organization,
391
+ },
392
+ isp: loc.isp,
393
+ domain: loc.domain,
394
+ },
395
+ }
396
+ : undefined;
397
+ const traits = {};
398
+ transfer(traits, event.user, ["id", "anonymous_id"]);
399
+ const properties = {};
400
+ transfer(properties, event, [
401
+ TableNameParameter,
402
+ "anon_ip",
403
+ "api_key",
404
+ "click_id",
405
+ "doc_encoding",
406
+ "doc_host",
407
+ "doc_path",
408
+ "doc_search",
409
+ "eventn_ctx_event_id",
410
+ "event_type",
411
+ "local_tz_offset",
412
+ "page_title",
413
+ "referer",
414
+ "screen_resolution",
415
+ "source_ip",
416
+ "url",
417
+ "user",
418
+ "location",
419
+ "parsed_ua",
420
+ "user_agent",
421
+ "user_language",
422
+ "utc_time",
423
+ "_timestamp",
424
+ "utm",
425
+ "vp_size",
426
+ ]);
427
+ if (type === "page") {
428
+ properties.url = event.url;
429
+ properties.title = event.page_title;
430
+ properties.referrer = event.referer;
431
+ properties.search = event.doc_search;
432
+ properties.host = event.doc_host;
433
+ properties.path = event.doc_path;
434
+ properties.width = parseInt(event.vp_size?.split("x")[0]);
435
+ properties.height = parseInt(event.vp_size?.split("x")[1]);
436
+ }
437
+ const screen = {};
438
+ const sr = event.screen_resolution?.split("x");
439
+ if (sr?.length === 2) {
440
+ screen.width = parseInt(sr[0]);
441
+ screen.height = parseInt(sr[1]);
442
+ }
443
+ const vs = event.vp_size?.split("x");
444
+ if (vs?.length === 2) {
445
+ screen.innerWidth = parseInt(vs[0]);
446
+ screen.innerHeight = parseInt(vs[1]);
447
+ }
448
+ return removeUndefined({
449
+ [TableNameParameter]: event[TableNameParameter],
450
+ messageId: event.eventn_ctx_event_id,
451
+ userId: event.user?.id,
452
+ anonymousId: event.user?.anonymous_id,
453
+ timestamp: event.utc_time,
454
+ receivedAt: event._timestamp,
455
+ writeKey: event.api_key,
456
+ type,
457
+ event: eventName,
458
+ context: {
459
+ ip: event.source_ip,
460
+ locale: event.user_language,
461
+ userAgent: event.user_agent,
462
+ page: {
463
+ url: event.url,
464
+ title: event.page_title,
465
+ referrer: event.referer,
466
+ search: event.doc_search,
467
+ host: event.doc_host,
468
+ path: event.doc_path,
469
+ encoding: event.doc_encoding,
470
+ timezoneOffset: event.local_tz_offset,
471
+ },
472
+ screen: Object.keys(screen).length > 0 ? screen : undefined,
473
+ clientIds,
474
+ campaign: event.utm,
475
+ traits,
476
+ geo,
477
+ },
478
+ properties,
479
+ traits: type === "identify" || type === "group" ? traits : undefined,
480
+ });
481
+ }
49
482
 
50
- export { DropRetryErrorName, HTTPError, RetryError, RetryErrorName };
483
+ export { DropRetryErrorName, HTTPError, RetryError, RetryErrorName, TableNameParameter, fromJitsuClassic, idToClassic, idToSnakeCaseFast, removeUndefined, toClassic, toJitsuClassic, toSnakeCase, transfer, transferAsClassic, transferAsSnakeCase, transferValue, transferValueAsSnakeCase };
@@ -1,3 +1,6 @@
1
+ import { AnyEvent, FullContext } from "@jitsu/protocols/functions";
2
+ import { AnalyticsServerEvent } from "@jitsu/protocols/analytics";
3
+ export declare const TableNameParameter = "JITSU_TABLE_NAME";
1
4
  export declare const DropRetryErrorName = "Drop & RetryError";
2
5
  export declare const RetryErrorName = "RetryError";
3
6
  export declare class RetryError extends Error {
@@ -26,3 +29,5 @@ export declare class HTTPError extends Error {
26
29
  response: string;
27
30
  };
28
31
  }
32
+ export declare function toJitsuClassic(event: AnalyticsServerEvent, ctx: FullContext): AnyEvent;
33
+ export declare function fromJitsuClassic(event: AnyEvent): AnyEvent;
@@ -1,6 +1,8 @@
1
1
  export declare function toSnakeCase(param: any): any;
2
+ export declare function toClassic(param: any): any;
2
3
  export declare function removeUndefined(param: any): any;
3
4
  export declare function transferAsSnakeCase(target: Record<string, any>, source: any, omit?: string[]): void;
5
+ export declare function transferAsClassic(target: Record<string, any>, source: any, omit?: string[]): void;
4
6
  export declare function transferValueAsSnakeCase(target: Record<string, any>, property: string, source: any): void;
5
7
  export declare function transfer(target: Record<string, any>, source: any, omit?: string[]): void;
6
8
  export declare function transferValue(target: Record<string, any>, property: string, source: any): void;
@@ -1 +1,2 @@
1
1
  export declare function idToSnakeCaseFast(id: string): string;
2
+ export declare function idToClassic(id: string): string;
package/jest.config.js ADDED
@@ -0,0 +1,8 @@
1
+ /** @type {import("ts-jest").JestConfigWithTsJest} */
2
+ module.exports = {
3
+ preset: "ts-jest",
4
+ testMatch: ["**/__tests__/**/*.test.ts"],
5
+ testEnvironment: "node",
6
+ runner: "jest-runner",
7
+ "setupFiles": ["./jest.setup.js"]
8
+ };
package/jest.setup.js ADDED
@@ -0,0 +1,8 @@
1
+
2
+ global.console = {
3
+ log: message => process.stdout.write(message + '\n'),
4
+ error: console.error,
5
+ warn: console.warn,
6
+ info: console.info,
7
+ debug: console.debug,
8
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jitsu/functions-lib",
3
- "version": "1.9.12",
3
+ "version": "1.9.13",
4
4
  "main": "dist/index.cjs.js",
5
5
  "module": "dist/index.es.js",
6
6
  "types": "dist/index.d.ts",
@@ -14,17 +14,19 @@
14
14
  "devDependencies": {
15
15
  "rollup": "^4.24.3",
16
16
  "@rollup/plugin-typescript": "^11.1.5",
17
+ "@rollup/plugin-node-resolve": "^16.0.0",
18
+ "@rollup/plugin-commonjs": "^28.0.2",
17
19
  "@types/jest": "^29.1.1",
18
20
  "@types/node": "^18.15.3",
19
21
  "jest": "^29.1.2",
20
- "ts-jest": "29.0.5"
21
- },
22
- "dependencies": {
23
- "lodash": "^4.17.21",
24
- "tslib": "^2.6.3"
22
+ "ts-jest": "29.0.5",
23
+ "tslib": "^2.6.3",
24
+ "@jitsu/sdk-js": "^3.1.5",
25
+ "@jitsu/protocols": "1.9.13"
25
26
  },
26
27
  "scripts": {
27
28
  "compile": "tsc -p .",
28
- "build": "pnpm compile && rollup -c"
29
+ "build": "pnpm compile && rollup -c",
30
+ "test": "tsc -p . && jest --verbose"
29
31
  }
30
32
  }
package/rollup.config.js CHANGED
@@ -1,8 +1,10 @@
1
1
  const typescript = require("@rollup/plugin-typescript");
2
+ const resolve = require("@rollup/plugin-node-resolve");
3
+ const commonjs = require("@rollup/plugin-commonjs");
2
4
 
3
5
  module.exports = [
4
6
  {
5
- plugins: [typescript()],
7
+ plugins: [typescript(), resolve({ preferBuiltins: false }), commonjs()],
6
8
  input: ["./src/index.ts"],
7
9
  output: [
8
10
  { file: "dist/index.es.js", format: "es" },
package/src/index.ts CHANGED
@@ -1 +1,3 @@
1
1
  export * from "./lib/functions";
2
+ export * from "./lib/objects";
3
+ export * from "./lib/strings";