@codeleap/analytics 5.0.11 → 5.0.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/package.json +4 -2
- package/package.json.bak +3 -1
- package/src/Analytics/index.ts +60 -0
- package/src/Analytics/types.ts +21 -0
- package/src/index.ts +2 -1
- package/src/obfuscate.ts +76 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@codeleap/analytics",
|
|
3
|
-
"version": "5.0.
|
|
3
|
+
"version": "5.0.13",
|
|
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.
|
|
12
|
+
"@codeleap/config": "5.0.13",
|
|
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.13",
|
|
20
|
+
"@codeleap/types": "5.0.13",
|
|
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.
|
|
3
|
+
"version": "5.0.13",
|
|
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'
|
package/src/obfuscate.ts
ADDED
|
@@ -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
|
+
}
|