@codeleap/analytics 5.0.11 → 5.0.12

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codeleap/analytics",
3
- "version": "5.0.11",
3
+ "version": "5.0.12",
4
4
  "main": "src/index.ts",
5
5
  "license": "UNLICENSED",
6
6
  "repository": {
@@ -9,13 +9,15 @@
9
9
  "directory": "packages/analytics"
10
10
  },
11
11
  "devDependencies": {
12
- "@codeleap/config": "5.0.11",
12
+ "@codeleap/config": "5.0.12",
13
13
  "ts-node-dev": "1.1.8"
14
14
  },
15
15
  "scripts": {
16
16
  "build": "echo 'No build needed'"
17
17
  },
18
18
  "peerDependencies": {
19
+ "@codeleap/utils": "5.0.12",
20
+ "@codeleap/types": "5.0.12",
19
21
  "typescript": "5.5.2"
20
22
  }
21
23
  }
package/package.json.bak CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codeleap/analytics",
3
- "version": "5.0.11",
3
+ "version": "5.0.12",
4
4
  "main": "src/index.ts",
5
5
  "license": "UNLICENSED",
6
6
  "repository": {
@@ -16,6 +16,8 @@
16
16
  "build": "echo 'No build needed'"
17
17
  },
18
18
  "peerDependencies": {
19
+ "@codeleap/utils": "workspace:*",
20
+ "@codeleap/types": "workspace:*",
19
21
  "typescript": "5.5.2"
20
22
  }
21
23
  }
@@ -0,0 +1,60 @@
1
+ import { AnyFunction, AnyRecord } from '@codeleap/types'
2
+ import { obfuscate } from '../obfuscate'
3
+ import { AnalyticsFunctions, AnalyticsObject, AnalyticsOptions } from './types'
4
+
5
+ export * from './types'
6
+
7
+ export class Analytics<Events extends AnyRecord> {
8
+ private enabled: boolean
9
+
10
+ private prepareAnalyticsData() {
11
+ const data = this.options.prepareData()
12
+ return data
13
+ }
14
+
15
+ private obfuscateAnalyticsData(data: any) {
16
+ return obfuscate({
17
+ object: data,
18
+ keys: this.options.obfuscateKeys || [],
19
+ values: this.options.obfuscateValues || [],
20
+ })
21
+ }
22
+
23
+ constructor(
24
+ private options: AnalyticsOptions,
25
+ private functions: AnalyticsFunctions
26
+ ) {
27
+ this.enabled = options.enabled
28
+
29
+ if (options.enabled) {
30
+ this.options.init()
31
+ }
32
+ }
33
+
34
+ event<T extends keyof Events>(name: T, data: Events[T] = {} as Events[T]) {
35
+ this.handle(name, data, 'event', this.functions.onEvent)
36
+ }
37
+
38
+ interaction<T extends keyof Events>(name: T, data: Events[T] = {} as Events[T]) {
39
+ this.handle(name, data, 'interaction', this.functions.onInteraction)
40
+ }
41
+
42
+ private handle(name: keyof Events, data: any, type: AnalyticsObject['type'], fn: AnyFunction) {
43
+ if (!this.enabled) return
44
+
45
+ try {
46
+ const obfuscated = this.obfuscateAnalyticsData({
47
+ ...data,
48
+ ...this.prepareAnalyticsData(),
49
+ })
50
+
51
+ fn({
52
+ name,
53
+ type,
54
+ data: obfuscated,
55
+ })
56
+ } catch (e) {
57
+ this.functions.onError(e)
58
+ }
59
+ }
60
+ }
@@ -0,0 +1,21 @@
1
+ import { Matcher } from '@codeleap/types'
2
+
3
+ export type AnalyticsObject = {
4
+ name: string
5
+ type: 'interaction' | 'event'
6
+ data: any
7
+ }
8
+
9
+ export type AnalyticsFunctions = {
10
+ onError: (err: any) => void
11
+ onInteraction: (obj: AnalyticsObject) => void
12
+ onEvent: (obj: AnalyticsObject) => void
13
+ }
14
+
15
+ export type AnalyticsOptions = {
16
+ enabled: boolean
17
+ obfuscateKeys: Matcher<'key'>[]
18
+ obfuscateValues: Matcher<'value'>[]
19
+ init: () => void
20
+ prepareData: () => any
21
+ }
package/src/index.ts CHANGED
@@ -1 +1,2 @@
1
- export * from './Segment'
1
+ export * from './Segment'
2
+ export * from './Analytics'
@@ -0,0 +1,76 @@
1
+ import { cloneDeep } from '@codeleap/utils'
2
+ import { Matcher } from '@codeleap/types'
3
+ import { inspect } from 'util'
4
+ import parse from 'url-parse'
5
+
6
+ type ObfuscateArgs = {
7
+ object: any
8
+ keys: (Matcher<'key'>)[]
9
+ values: (Matcher<'value'>)[]
10
+ }
11
+
12
+ function removeKey(obj, key) {
13
+ if (obj?.hasOwnProperty(key)) {
14
+ obj[key] = '[secret]'
15
+ }
16
+ for (const subObj in obj) {
17
+ if (typeof obj[subObj] == 'object') {
18
+ removeKey(obj[subObj], key)
19
+ }
20
+ }
21
+ }
22
+
23
+ function removeValue(obj, value) {
24
+ for (const subObj in obj) {
25
+ const isString = typeof obj[subObj] == 'string'
26
+ if (isString) {
27
+ const isRegex = value instanceof RegExp
28
+ const match = isRegex ? value.test(obj[subObj]) : obj[subObj].includes(value)
29
+ if (match) {
30
+ if (obj[subObj].startsWith('http')) {
31
+ const url = parse(obj[subObj])
32
+ obj[subObj] = `${url.origin}${url.pathname}/[secret]`
33
+ } else {
34
+ obj[subObj] = '[secret]'
35
+ }
36
+ }
37
+ }
38
+ if (typeof obj[subObj] == 'object') {
39
+ removeValue(obj[subObj], value)
40
+ }
41
+ }
42
+ }
43
+
44
+ export function obfuscate(args: ObfuscateArgs) {
45
+ const { object, keys, values } = args
46
+
47
+ let isCircular = false
48
+ try {
49
+ JSON.stringify(args)
50
+ } catch (e) {
51
+ isCircular = true
52
+ }
53
+
54
+ if (typeof object === 'object' && !isCircular) {
55
+ let cleanData = {}
56
+ try {
57
+ cleanData = cloneDeep(object)
58
+ keys.forEach(fieldName => removeKey(cleanData, fieldName))
59
+ values.forEach(fieldName => removeValue(cleanData, fieldName))
60
+ } catch (err1) {
61
+ try {
62
+ cleanData = inspect(object, { depth: 1 })
63
+ } catch (err2) {
64
+ cleanData = { value: `Couldn't process data` }
65
+ }
66
+ }
67
+ const result = cleanData
68
+ return result
69
+ } else {
70
+ if (isCircular) {
71
+ return { ...args.object, WARNING: 'Circular reference detected' }
72
+ } else {
73
+ return args.object
74
+ }
75
+ }
76
+ }