@leanbase.com/js 0.1.2 → 0.2.0-alpha.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.
Files changed (136) hide show
  1. package/dist/autocapture-utils.d.ts +17 -0
  2. package/dist/autocapture.d.ts +35 -0
  3. package/dist/config.d.ts +5 -0
  4. package/dist/constants.d.ts +54 -0
  5. package/dist/entrypoints/main.cjs.d.ts +4 -0
  6. package/dist/entrypoints/module.es.d.ts +4 -0
  7. package/dist/extensions/rageclick.d.ts +9 -0
  8. package/dist/iife.d.ts +19 -0
  9. package/dist/index.d.ts +2 -779
  10. package/dist/leanbase-logger.d.ts +6 -0
  11. package/dist/leanbase-persistence.d.ts +64 -0
  12. package/dist/leanbase.d.ts +49 -0
  13. package/dist/leanbase.iife.js +1 -4745
  14. package/dist/leanbase.iife.js.map +1 -1
  15. package/dist/main.js +2 -0
  16. package/dist/main.js.map +1 -0
  17. package/dist/module.d.ts +780 -0
  18. package/dist/module.js +2 -0
  19. package/dist/module.js.map +1 -0
  20. package/dist/page-view.d.ts +29 -0
  21. package/dist/scroll-manager.d.ts +21 -0
  22. package/dist/session-props.d.ts +32 -0
  23. package/dist/sessionid.d.ts +50 -0
  24. package/dist/storage.d.ts +24 -0
  25. package/{src/types.ts → dist/types.d.ts} +145 -235
  26. package/dist/utils/blocked-uas.d.ts +17 -0
  27. package/dist/utils/element-utils.d.ts +5 -0
  28. package/dist/utils/event-utils.d.ts +22 -0
  29. package/dist/utils/index.d.ts +44 -0
  30. package/dist/utils/request-utils.d.ts +12 -0
  31. package/dist/utils/simple-event-emitter.d.ts +6 -0
  32. package/dist/utils/user-agent-utils.d.ts +18 -0
  33. package/dist/uuidv7.d.ts +43 -0
  34. package/dist/version.d.ts +1 -0
  35. package/lib/autocapture-utils.d.ts +17 -0
  36. package/{src/autocapture-utils.ts → lib/autocapture-utils.js} +196 -280
  37. package/lib/autocapture-utils.js.map +1 -0
  38. package/lib/autocapture.d.ts +35 -0
  39. package/lib/autocapture.js +311 -0
  40. package/lib/autocapture.js.map +1 -0
  41. package/lib/config.d.ts +5 -0
  42. package/lib/config.js +7 -0
  43. package/lib/config.js.map +1 -0
  44. package/lib/constants.d.ts +54 -0
  45. package/{src/constants.ts → lib/constants.js} +58 -55
  46. package/lib/constants.js.map +1 -0
  47. package/lib/entrypoints/main.cjs.d.ts +4 -0
  48. package/lib/entrypoints/main.cjs.js +3 -0
  49. package/lib/entrypoints/main.cjs.js.map +1 -0
  50. package/lib/entrypoints/module.es.d.ts +4 -0
  51. package/lib/entrypoints/module.es.js +3 -0
  52. package/lib/entrypoints/module.es.js.map +1 -0
  53. package/lib/extensions/rageclick.d.ts +9 -0
  54. package/lib/extensions/rageclick.js +27 -0
  55. package/lib/extensions/rageclick.js.map +1 -0
  56. package/lib/iife.d.ts +19 -0
  57. package/lib/iife.js +67 -0
  58. package/lib/iife.js.map +1 -0
  59. package/{src/index.ts → lib/index.d.ts} +2 -2
  60. package/lib/index.js +2 -0
  61. package/lib/index.js.map +1 -0
  62. package/lib/leanbase-logger.d.ts +6 -0
  63. package/lib/leanbase-logger.js +25 -0
  64. package/lib/leanbase-logger.js.map +1 -0
  65. package/lib/leanbase-persistence.d.ts +64 -0
  66. package/lib/leanbase-persistence.js +287 -0
  67. package/lib/leanbase-persistence.js.map +1 -0
  68. package/lib/leanbase.d.ts +49 -0
  69. package/lib/leanbase.js +294 -0
  70. package/lib/leanbase.js.map +1 -0
  71. package/lib/page-view.d.ts +29 -0
  72. package/lib/page-view.js +81 -0
  73. package/lib/page-view.js.map +1 -0
  74. package/lib/scroll-manager.d.ts +21 -0
  75. package/lib/scroll-manager.js +79 -0
  76. package/lib/scroll-manager.js.map +1 -0
  77. package/lib/session-props.d.ts +32 -0
  78. package/lib/session-props.js +73 -0
  79. package/lib/session-props.js.map +1 -0
  80. package/lib/sessionid.d.ts +50 -0
  81. package/{src/sessionid.ts → lib/sessionid.js} +128 -204
  82. package/lib/sessionid.js.map +1 -0
  83. package/lib/storage.d.ts +24 -0
  84. package/{src/storage.ts → lib/storage.js} +182 -225
  85. package/lib/storage.js.map +1 -0
  86. package/lib/types.d.ts +544 -0
  87. package/lib/types.js +7 -0
  88. package/lib/types.js.map +1 -0
  89. package/lib/utils/blocked-uas.d.ts +17 -0
  90. package/{src/utils/blocked-uas.ts → lib/utils/blocked-uas.js} +19 -48
  91. package/lib/utils/blocked-uas.js.map +1 -0
  92. package/lib/utils/element-utils.d.ts +5 -0
  93. package/{src/utils/element-utils.ts → lib/utils/element-utils.js} +13 -17
  94. package/lib/utils/element-utils.js.map +1 -0
  95. package/lib/utils/event-utils.d.ts +22 -0
  96. package/lib/utils/event-utils.js +258 -0
  97. package/lib/utils/event-utils.js.map +1 -0
  98. package/lib/utils/index.d.ts +44 -0
  99. package/lib/utils/index.js +183 -0
  100. package/lib/utils/index.js.map +1 -0
  101. package/lib/utils/request-utils.d.ts +12 -0
  102. package/lib/utils/request-utils.js +107 -0
  103. package/lib/utils/request-utils.js.map +1 -0
  104. package/lib/utils/simple-event-emitter.d.ts +6 -0
  105. package/lib/utils/simple-event-emitter.js +24 -0
  106. package/lib/utils/simple-event-emitter.js.map +1 -0
  107. package/lib/utils/user-agent-utils.d.ts +18 -0
  108. package/lib/utils/user-agent-utils.js +369 -0
  109. package/lib/utils/user-agent-utils.js.map +1 -0
  110. package/lib/uuidv7.d.ts +43 -0
  111. package/{src/uuidv7.ts → lib/uuidv7.js} +103 -131
  112. package/lib/uuidv7.js.map +1 -0
  113. package/lib/version.d.ts +1 -0
  114. package/lib/version.js +2 -0
  115. package/lib/version.js.map +1 -0
  116. package/package.json +23 -11
  117. package/dist/index.cjs +0 -3032
  118. package/dist/index.cjs.map +0 -1
  119. package/dist/index.mjs +0 -3030
  120. package/dist/index.mjs.map +0 -1
  121. package/src/autocapture.ts +0 -415
  122. package/src/config.ts +0 -8
  123. package/src/extensions/rageclick.ts +0 -34
  124. package/src/iife.ts +0 -87
  125. package/src/leanbase-logger.ts +0 -26
  126. package/src/leanbase-persistence.ts +0 -374
  127. package/src/leanbase.ts +0 -424
  128. package/src/page-view.ts +0 -124
  129. package/src/scroll-manager.ts +0 -103
  130. package/src/session-props.ts +0 -114
  131. package/src/utils/event-utils.ts +0 -304
  132. package/src/utils/index.ts +0 -222
  133. package/src/utils/request-utils.ts +0 -128
  134. package/src/utils/simple-event-emitter.ts +0 -27
  135. package/src/utils/user-agent-utils.ts +0 -357
  136. package/src/version.ts +0 -1
@@ -1,65 +1,54 @@
1
- import { AutocaptureConfig, LeanbaseConfig, Properties } from './types'
2
- import { each, entries } from './utils'
3
-
4
- import { isNullish, isString, isUndefined, isArray, isBoolean } from '@posthog/core'
5
- import { isDocumentFragment, isElementNode, isTag, isTextNode } from './utils/element-utils'
6
- import { includes, trim } from '@posthog/core'
7
- import { logger } from './leanbase-logger'
8
-
9
- export function splitClassString(s: string): string[] {
10
- return s ? trim(s).split(/\s+/) : []
1
+ import { each, entries } from './utils';
2
+ import { isNullish, isString, isUndefined, isArray, isBoolean } from '@posthog/core';
3
+ import { isDocumentFragment, isElementNode, isTag, isTextNode } from './utils/element-utils';
4
+ import { includes, trim } from '@posthog/core';
5
+ import { logger } from './leanbase-logger';
6
+ export function splitClassString(s) {
7
+ return s ? trim(s).split(/\s+/) : [];
11
8
  }
12
-
13
- function checkForURLMatches(urlsList: (string | RegExp)[]): boolean {
14
- const url = window?.location.href
15
- return !!(url && urlsList && urlsList.some((regex) => url.match(regex)))
9
+ function checkForURLMatches(urlsList) {
10
+ const url = window?.location.href;
11
+ return !!(url && urlsList && urlsList.some((regex) => url.match(regex)));
16
12
  }
17
-
18
13
  /*
19
14
  * Get the className of an element, accounting for edge cases where element.className is an object
20
15
  *
21
16
  * Because this is a string it can contain unexpected characters
22
17
  * So, this method safely splits the className and returns that array.
23
18
  */
24
- export function getClassNames(el: Element): string[] {
25
- let className = ''
19
+ export function getClassNames(el) {
20
+ let className = '';
26
21
  switch (typeof el.className) {
27
22
  case 'string':
28
- className = el.className
29
- break
23
+ className = el.className;
24
+ break;
30
25
  // TODO: when is this ever used?
31
26
  case 'object': // handle cases where className might be SVGAnimatedString or some other type
32
27
  className =
33
- (el.className && 'baseVal' in el.className ? (el.className as any).baseVal : null) ||
34
- el.getAttribute('class') ||
35
- ''
36
- break
28
+ (el.className && 'baseVal' in el.className ? el.className.baseVal : null) ||
29
+ el.getAttribute('class') ||
30
+ '';
31
+ break;
37
32
  default:
38
- className = ''
33
+ className = '';
39
34
  }
40
-
41
- return splitClassString(className)
35
+ return splitClassString(className);
42
36
  }
43
-
44
- export function makeSafeText(s: string | null | undefined): string | null {
37
+ export function makeSafeText(s) {
45
38
  if (isNullish(s)) {
46
- return null
47
- }
48
-
49
- return (
50
- trim(s)
51
- // scrub potentially sensitive values
52
- .split(/(\s+)/)
53
- .filter((s) => shouldCaptureValue(s))
54
- .join('')
55
- // normalize whitespace
56
- .replace(/[\r\n]/g, ' ')
57
- .replace(/[ ]+/g, ' ')
58
- // truncate
59
- .substring(0, 255)
60
- )
39
+ return null;
40
+ }
41
+ return (trim(s)
42
+ // scrub potentially sensitive values
43
+ .split(/(\s+)/)
44
+ .filter((s) => shouldCaptureValue(s))
45
+ .join('')
46
+ // normalize whitespace
47
+ .replace(/[\r\n]/g, ' ')
48
+ .replace(/[ ]+/g, ' ')
49
+ // truncate
50
+ .substring(0, 255));
61
51
  }
62
-
63
52
  /*
64
53
  * Get the direct text content of an element, protecting against sensitive data collection.
65
54
  * Concats textContent of each of the element's text node children; this avoids potential
@@ -69,147 +58,129 @@ export function makeSafeText(s: string | null | undefined): string | null {
69
58
  * @param {Element} el - element to get the text of
70
59
  * @returns {string} the element's direct text content
71
60
  */
72
- export function getSafeText(el: Element): string {
73
- let elText = ''
74
-
61
+ export function getSafeText(el) {
62
+ let elText = '';
75
63
  if (shouldCaptureElement(el) && !isSensitiveElement(el) && el.childNodes && el.childNodes.length) {
76
64
  each(el.childNodes, function (child) {
77
65
  if (isTextNode(child) && child.textContent) {
78
- elText += makeSafeText(child.textContent) ?? ''
66
+ elText += makeSafeText(child.textContent) ?? '';
79
67
  }
80
- })
68
+ });
81
69
  }
82
-
83
- return trim(elText)
70
+ return trim(elText);
84
71
  }
85
-
86
- export function getEventTarget(e: Event): Element | null {
72
+ export function getEventTarget(e) {
87
73
  // https://developer.mozilla.org/en-US/docs/Web/API/Event/target#Compatibility_notes
88
74
  if (isUndefined(e.target)) {
89
- return (e.srcElement as Element) || null
90
- } else {
91
- if ((e.target as HTMLElement)?.shadowRoot) {
92
- return (e.composedPath()[0] as Element) || null
75
+ return e.srcElement || null;
76
+ }
77
+ else {
78
+ if (e.target?.shadowRoot) {
79
+ return e.composedPath()[0] || null;
93
80
  }
94
- return (e.target as Element) || null
81
+ return e.target || null;
95
82
  }
96
83
  }
97
-
98
- export const autocaptureCompatibleElements = ['a', 'button', 'form', 'input', 'select', 'textarea', 'label']
99
-
84
+ export const autocaptureCompatibleElements = ['a', 'button', 'form', 'input', 'select', 'textarea', 'label'];
100
85
  /*
101
86
  if there is no config, then all elements are allowed
102
87
  if there is a config, and there is an allow list, then only elements in the allow list are allowed
103
88
  assumes that some other code is checking this element's parents
104
89
  */
105
- function checkIfElementTreePassesElementAllowList(
106
- elements: Element[],
107
- autocaptureConfig: AutocaptureConfig | undefined
108
- ): boolean {
109
- const allowlist = autocaptureConfig?.element_allowlist
90
+ function checkIfElementTreePassesElementAllowList(elements, autocaptureConfig) {
91
+ const allowlist = autocaptureConfig?.element_allowlist;
110
92
  if (isUndefined(allowlist)) {
111
93
  // everything is allowed, when there is no allow list
112
- return true
94
+ return true;
113
95
  }
114
-
115
96
  // check each element in the tree
116
97
  // if any of the elements are in the allow list, then the tree is allowed
117
98
  for (const el of elements) {
118
99
  if (allowlist.some((elementType) => el.tagName.toLowerCase() === elementType)) {
119
- return true
100
+ return true;
120
101
  }
121
102
  }
122
-
123
103
  // otherwise there is an allow list and this element tree didn't match it
124
- return false
104
+ return false;
125
105
  }
126
-
127
106
  /*
128
107
  if there is no selector list (i.e. it is undefined), then any elements matches
129
108
  if there is an empty list, then no elements match
130
109
  if there is a selector list, then check it against each element provided
131
110
  */
132
- function checkIfElementsMatchCSSSelector(elements: Element[], selectorList: string[] | undefined): boolean {
111
+ function checkIfElementsMatchCSSSelector(elements, selectorList) {
133
112
  if (isUndefined(selectorList)) {
134
113
  // everything is allowed, when there is no selector list
135
- return true
114
+ return true;
136
115
  }
137
-
138
116
  for (const el of elements) {
139
117
  if (selectorList.some((selector) => el.matches(selector))) {
140
- return true
118
+ return true;
141
119
  }
142
120
  }
143
-
144
- return false
121
+ return false;
145
122
  }
146
-
147
- export function getParentElement(curEl: Element): Element | false {
148
- const parentNode = curEl.parentNode
149
- if (!parentNode || !isElementNode(parentNode)) return false
150
- return parentNode
123
+ export function getParentElement(curEl) {
124
+ const parentNode = curEl.parentNode;
125
+ if (!parentNode || !isElementNode(parentNode))
126
+ return false;
127
+ return parentNode;
151
128
  }
152
-
153
129
  // autocapture check will already filter for ph-no-capture,
154
130
  // but we include it here to protect against future changes accidentally removing that check
155
- const DEFAULT_RAGE_CLICK_IGNORE_LIST = ['.ph-no-rageclick', '.ph-no-capture']
156
- export function shouldCaptureRageclick(el: Element | null, _config: LeanbaseConfig['rageclick']) {
131
+ const DEFAULT_RAGE_CLICK_IGNORE_LIST = ['.ph-no-rageclick', '.ph-no-capture'];
132
+ export function shouldCaptureRageclick(el, _config) {
157
133
  if (!window || cannotCheckForAutocapture(el)) {
158
- return false
134
+ return false;
159
135
  }
160
-
161
- let selectorIgnoreList: string[] | boolean
136
+ let selectorIgnoreList;
162
137
  if (isBoolean(_config)) {
163
- selectorIgnoreList = _config ? DEFAULT_RAGE_CLICK_IGNORE_LIST : false
164
- } else {
165
- selectorIgnoreList = _config?.css_selector_ignorelist ?? DEFAULT_RAGE_CLICK_IGNORE_LIST
138
+ selectorIgnoreList = _config ? DEFAULT_RAGE_CLICK_IGNORE_LIST : false;
139
+ }
140
+ else {
141
+ selectorIgnoreList = _config?.css_selector_ignorelist ?? DEFAULT_RAGE_CLICK_IGNORE_LIST;
166
142
  }
167
-
168
143
  if (selectorIgnoreList === false) {
169
- return false
144
+ return false;
170
145
  }
171
-
172
- const { targetElementList } = getElementAndParentsForElement(el, false)
146
+ const { targetElementList } = getElementAndParentsForElement(el, false);
173
147
  // we don't capture if we match the ignore list
174
- return !checkIfElementsMatchCSSSelector(targetElementList, selectorIgnoreList)
148
+ return !checkIfElementsMatchCSSSelector(targetElementList, selectorIgnoreList);
175
149
  }
176
-
177
- const cannotCheckForAutocapture = (el: Element | null) => {
178
- return !el || isTag(el, 'html') || !isElementNode(el)
179
- }
180
-
181
- const getElementAndParentsForElement = (el: Element, captureOnAnyElement: false | true | undefined) => {
150
+ const cannotCheckForAutocapture = (el) => {
151
+ return !el || isTag(el, 'html') || !isElementNode(el);
152
+ };
153
+ const getElementAndParentsForElement = (el, captureOnAnyElement) => {
182
154
  if (!window || cannotCheckForAutocapture(el)) {
183
- return { parentIsUsefulElement: false, targetElementList: [] }
155
+ return { parentIsUsefulElement: false, targetElementList: [] };
184
156
  }
185
-
186
- let parentIsUsefulElement = false
187
- const targetElementList: Element[] = [el]
188
- let curEl: Element = el
157
+ let parentIsUsefulElement = false;
158
+ const targetElementList = [el];
159
+ let curEl = el;
189
160
  while (curEl.parentNode && !isTag(curEl, 'body')) {
190
161
  // If element is a shadow root, we skip it
191
162
  if (isDocumentFragment(curEl.parentNode)) {
192
- targetElementList.push((curEl.parentNode as any).host)
193
- curEl = (curEl.parentNode as any).host
194
- continue
163
+ targetElementList.push(curEl.parentNode.host);
164
+ curEl = curEl.parentNode.host;
165
+ continue;
195
166
  }
196
- const parentNode = getParentElement(curEl)
197
- if (!parentNode) break
167
+ const parentNode = getParentElement(curEl);
168
+ if (!parentNode)
169
+ break;
198
170
  if (captureOnAnyElement || autocaptureCompatibleElements.indexOf(parentNode.tagName.toLowerCase()) > -1) {
199
- parentIsUsefulElement = true
200
- } else {
201
- const compStyles = window.getComputedStyle(parentNode)
171
+ parentIsUsefulElement = true;
172
+ }
173
+ else {
174
+ const compStyles = window.getComputedStyle(parentNode);
202
175
  if (compStyles && compStyles.getPropertyValue('cursor') === 'pointer') {
203
- parentIsUsefulElement = true
176
+ parentIsUsefulElement = true;
204
177
  }
205
178
  }
206
-
207
- targetElementList.push(parentNode)
208
- curEl = parentNode
179
+ targetElementList.push(parentNode);
180
+ curEl = parentNode;
209
181
  }
210
- return { parentIsUsefulElement, targetElementList }
211
- }
212
-
182
+ return { parentIsUsefulElement, targetElementList };
183
+ };
213
184
  /*
214
185
  * Check whether a DOM event should be "captured" or if it may contain sensitive data
215
186
  * using a variety of heuristics.
@@ -220,153 +191,126 @@ const getElementAndParentsForElement = (el: Element, captureOnAnyElement: false
220
191
  * @param {string[]} allowedEventTypes - event types to capture, normally just 'click', but some autocapture types react to different events, some elements have fixed events (e.g., form has "submit")
221
192
  * @returns {boolean} whether the event should be captured
222
193
  */
223
- export function shouldCaptureDomEvent(
224
- el: Element,
225
- event: Event,
226
- autocaptureConfig: AutocaptureConfig | undefined = undefined,
227
- captureOnAnyElement?: boolean,
228
- allowedEventTypes?: string[]
229
- ): boolean {
194
+ export function shouldCaptureDomEvent(el, event, autocaptureConfig = undefined, captureOnAnyElement, allowedEventTypes) {
230
195
  if (!window || cannotCheckForAutocapture(el)) {
231
- return false
196
+ return false;
232
197
  }
233
-
234
198
  if (autocaptureConfig?.url_allowlist) {
235
199
  // if the current URL is not in the allow list, don't capture
236
200
  if (!checkForURLMatches(autocaptureConfig.url_allowlist)) {
237
- return false
201
+ return false;
238
202
  }
239
203
  }
240
-
241
204
  if (autocaptureConfig?.url_ignorelist) {
242
205
  // if the current URL is in the ignore list, don't capture
243
206
  if (checkForURLMatches(autocaptureConfig.url_ignorelist)) {
244
- return false
207
+ return false;
245
208
  }
246
209
  }
247
-
248
210
  if (autocaptureConfig?.dom_event_allowlist) {
249
- const allowlist = autocaptureConfig.dom_event_allowlist
211
+ const allowlist = autocaptureConfig.dom_event_allowlist;
250
212
  if (allowlist && !allowlist.some((eventType) => event.type === eventType)) {
251
- return false
213
+ return false;
252
214
  }
253
215
  }
254
-
255
- const { parentIsUsefulElement, targetElementList } = getElementAndParentsForElement(el, captureOnAnyElement)
256
-
216
+ const { parentIsUsefulElement, targetElementList } = getElementAndParentsForElement(el, captureOnAnyElement);
257
217
  if (!checkIfElementTreePassesElementAllowList(targetElementList, autocaptureConfig)) {
258
- return false
218
+ return false;
259
219
  }
260
-
261
220
  if (!checkIfElementsMatchCSSSelector(targetElementList, autocaptureConfig?.css_selector_allowlist)) {
262
- return false
221
+ return false;
263
222
  }
264
-
265
- const compStyles = window.getComputedStyle(el)
223
+ const compStyles = window.getComputedStyle(el);
266
224
  if (compStyles && compStyles.getPropertyValue('cursor') === 'pointer' && event.type === 'click') {
267
- return true
225
+ return true;
268
226
  }
269
-
270
- const tag = el.tagName.toLowerCase()
227
+ const tag = el.tagName.toLowerCase();
271
228
  switch (tag) {
272
229
  case 'html':
273
- return false
230
+ return false;
274
231
  case 'form':
275
- return (allowedEventTypes || ['submit']).indexOf(event.type) >= 0
232
+ return (allowedEventTypes || ['submit']).indexOf(event.type) >= 0;
276
233
  case 'input':
277
234
  case 'select':
278
235
  case 'textarea':
279
- return (allowedEventTypes || ['change', 'click']).indexOf(event.type) >= 0
236
+ return (allowedEventTypes || ['change', 'click']).indexOf(event.type) >= 0;
280
237
  default:
281
- if (parentIsUsefulElement) return (allowedEventTypes || ['click']).indexOf(event.type) >= 0
282
- return (
283
- (allowedEventTypes || ['click']).indexOf(event.type) >= 0 &&
284
- (autocaptureCompatibleElements.indexOf(tag) > -1 || el.getAttribute('contenteditable') === 'true')
285
- )
238
+ if (parentIsUsefulElement)
239
+ return (allowedEventTypes || ['click']).indexOf(event.type) >= 0;
240
+ return ((allowedEventTypes || ['click']).indexOf(event.type) >= 0 &&
241
+ (autocaptureCompatibleElements.indexOf(tag) > -1 || el.getAttribute('contenteditable') === 'true'));
286
242
  }
287
243
  }
288
-
289
244
  /*
290
245
  * Check whether a DOM element should be "captured" or if it may contain sensitive data
291
246
  * using a variety of heuristics.
292
247
  * @param {Element} el - element to check
293
248
  * @returns {boolean} whether the element should be captured
294
249
  */
295
- export function shouldCaptureElement(el: Element): boolean {
296
- for (let curEl = el; curEl.parentNode && !isTag(curEl, 'body'); curEl = curEl.parentNode as Element) {
297
- const classes = getClassNames(curEl)
250
+ export function shouldCaptureElement(el) {
251
+ for (let curEl = el; curEl.parentNode && !isTag(curEl, 'body'); curEl = curEl.parentNode) {
252
+ const classes = getClassNames(curEl);
298
253
  if (includes(classes, 'ph-sensitive') || includes(classes, 'ph-no-capture')) {
299
- return false
254
+ return false;
300
255
  }
301
256
  }
302
-
303
257
  if (includes(getClassNames(el), 'ph-include')) {
304
- return true
258
+ return true;
305
259
  }
306
-
307
260
  // don't include hidden or password fields
308
- const type = (el as HTMLInputElement).type || ''
261
+ const type = el.type || '';
309
262
  if (isString(type)) {
310
263
  // it's possible for el.type to be a DOM element if el is a form with a child input[name="type"]
311
264
  switch (type.toLowerCase()) {
312
265
  case 'hidden':
313
- return false
266
+ return false;
314
267
  case 'password':
315
- return false
268
+ return false;
316
269
  }
317
270
  }
318
-
319
271
  // filter out data from fields that look like sensitive fields
320
- const name = (el as HTMLInputElement).name || el.id || ''
272
+ const name = el.name || el.id || '';
321
273
  // See https://github.com/posthog/posthog-js/issues/165
322
274
  // Under specific circumstances a bug caused .replace to be called on a DOM element
323
275
  // instead of a string, removing the element from the page. Ensure this issue is mitigated.
324
276
  if (isString(name)) {
325
277
  // it's possible for el.name or el.id to be a DOM element if el is a form with a child input[name="name"]
326
- const sensitiveNameRegex =
327
- /^cc|cardnum|ccnum|creditcard|csc|cvc|cvv|exp|pass|pwd|routing|seccode|securitycode|securitynum|socialsec|socsec|ssn/i
278
+ const sensitiveNameRegex = /^cc|cardnum|ccnum|creditcard|csc|cvc|cvv|exp|pass|pwd|routing|seccode|securitycode|securitynum|socialsec|socsec|ssn/i;
328
279
  if (sensitiveNameRegex.test(name.replace(/[^a-zA-Z0-9]/g, ''))) {
329
- return false
280
+ return false;
330
281
  }
331
282
  }
332
-
333
- return true
283
+ return true;
334
284
  }
335
-
336
285
  /*
337
286
  * Check whether a DOM element is 'sensitive' and we should only capture limited data
338
287
  * @param {Element} el - element to check
339
288
  * @returns {boolean} whether the element should be captured
340
289
  */
341
- export function isSensitiveElement(el: Element): boolean {
290
+ export function isSensitiveElement(el) {
342
291
  // don't send data from inputs or similar elements since there will always be
343
292
  // a risk of clientside javascript placing sensitive data in attributes
344
- const allowedInputTypes = ['button', 'checkbox', 'submit', 'reset']
345
- if (
346
- (isTag(el, 'input') && !allowedInputTypes.includes((el as HTMLInputElement).type)) ||
293
+ const allowedInputTypes = ['button', 'checkbox', 'submit', 'reset'];
294
+ if ((isTag(el, 'input') && !allowedInputTypes.includes(el.type)) ||
347
295
  isTag(el, 'select') ||
348
296
  isTag(el, 'textarea') ||
349
- el.getAttribute('contenteditable') === 'true'
350
- ) {
351
- return true
297
+ el.getAttribute('contenteditable') === 'true') {
298
+ return true;
352
299
  }
353
- return false
300
+ return false;
354
301
  }
355
-
356
302
  // Define the core pattern for matching credit card numbers
357
- const coreCCPattern = `(4[0-9]{12}(?:[0-9]{3})?)|(5[1-5][0-9]{14})|(6(?:011|5[0-9]{2})[0-9]{12})|(3[47][0-9]{13})|(3(?:0[0-5]|[68][0-9])[0-9]{11})|((?:2131|1800|35[0-9]{3})[0-9]{11})`
303
+ const coreCCPattern = `(4[0-9]{12}(?:[0-9]{3})?)|(5[1-5][0-9]{14})|(6(?:011|5[0-9]{2})[0-9]{12})|(3[47][0-9]{13})|(3(?:0[0-5]|[68][0-9])[0-9]{11})|((?:2131|1800|35[0-9]{3})[0-9]{11})`;
358
304
  // Create the Anchored version of the regex by adding '^' at the start and '$' at the end
359
- const anchoredCCRegex = new RegExp(`^(?:${coreCCPattern})$`)
305
+ const anchoredCCRegex = new RegExp(`^(?:${coreCCPattern})$`);
360
306
  // The Unanchored version is essentially the core pattern, usable as is for partial matches
361
- const unanchoredCCRegex = new RegExp(coreCCPattern)
362
-
307
+ const unanchoredCCRegex = new RegExp(coreCCPattern);
363
308
  // Define the core pattern for matching SSNs with optional dashes
364
- const coreSSNPattern = `\\d{3}-?\\d{2}-?\\d{4}`
309
+ const coreSSNPattern = `\\d{3}-?\\d{2}-?\\d{4}`;
365
310
  // Create the Anchored version of the regex by adding '^' at the start and '$' at the end
366
- const anchoredSSNRegex = new RegExp(`^(${coreSSNPattern})$`)
311
+ const anchoredSSNRegex = new RegExp(`^(${coreSSNPattern})$`);
367
312
  // The Unanchored version is essentially the core pattern itself, usable for partial matches
368
- const unanchoredSSNRegex = new RegExp(`(${coreSSNPattern})`)
369
-
313
+ const unanchoredSSNRegex = new RegExp(`(${coreSSNPattern})`);
370
314
  /*
371
315
  * Check whether a string value should be "captured" or if it may contain sensitive data
372
316
  * using a variety of heuristics.
@@ -374,31 +318,26 @@ const unanchoredSSNRegex = new RegExp(`(${coreSSNPattern})`)
374
318
  * @param {boolean} anchorRegexes - whether to anchor the regexes to the start and end of the string
375
319
  * @returns {boolean} whether the element should be captured
376
320
  */
377
- export function shouldCaptureValue(value: string, anchorRegexes = true): boolean {
321
+ export function shouldCaptureValue(value, anchorRegexes = true) {
378
322
  if (isNullish(value)) {
379
- return false
323
+ return false;
380
324
  }
381
-
382
325
  if (isString(value)) {
383
- value = trim(value)
384
-
326
+ value = trim(value);
385
327
  // check to see if input value looks like a credit card number
386
328
  // see: https://www.safaribooksonline.com/library/view/regular-expressions-cookbook/9781449327453/ch04s20.html
387
- const ccRegex = anchorRegexes ? anchoredCCRegex : unanchoredCCRegex
329
+ const ccRegex = anchorRegexes ? anchoredCCRegex : unanchoredCCRegex;
388
330
  if (ccRegex.test((value || '').replace(/[- ]/g, ''))) {
389
- return false
331
+ return false;
390
332
  }
391
-
392
333
  // check to see if input value looks like a social security number
393
- const ssnRegex = anchorRegexes ? anchoredSSNRegex : unanchoredSSNRegex
334
+ const ssnRegex = anchorRegexes ? anchoredSSNRegex : unanchoredSSNRegex;
394
335
  if (ssnRegex.test(value)) {
395
- return false
336
+ return false;
396
337
  }
397
338
  }
398
-
399
- return true
339
+ return true;
400
340
  }
401
-
402
341
  /*
403
342
  * Check whether an attribute name is an Angular style attr (either _ngcontent or _nghost)
404
343
  * These update on each build and lead to noise in the element chain
@@ -406,13 +345,12 @@ export function shouldCaptureValue(value: string, anchorRegexes = true): boolean
406
345
  * @param {string} attributeName - string value to check
407
346
  * @returns {boolean} whether the element is an angular tag
408
347
  */
409
- export function isAngularStyleAttr(attributeName: string): boolean {
348
+ export function isAngularStyleAttr(attributeName) {
410
349
  if (isString(attributeName)) {
411
- return attributeName.substring(0, 10) === '_ngcontent' || attributeName.substring(0, 7) === '_nghost'
350
+ return attributeName.substring(0, 10) === '_ngcontent' || attributeName.substring(0, 7) === '_nghost';
412
351
  }
413
- return false
352
+ return false;
414
353
  }
415
-
416
354
  /*
417
355
  * Iterate through children of a target element looking for span tags
418
356
  * and return the text content of the span tags, separated by spaces,
@@ -420,105 +358,82 @@ export function isAngularStyleAttr(attributeName: string): boolean {
420
358
  * @param {Element} target - element to check
421
359
  * @returns {string} text content of the target element and its child span tags
422
360
  */
423
- export function getDirectAndNestedSpanText(target: Element): string {
424
- let text = getSafeText(target)
425
- text = `${text} ${getNestedSpanText(target)}`.trim()
426
- return shouldCaptureValue(text) ? text : ''
361
+ export function getDirectAndNestedSpanText(target) {
362
+ let text = getSafeText(target);
363
+ text = `${text} ${getNestedSpanText(target)}`.trim();
364
+ return shouldCaptureValue(text) ? text : '';
427
365
  }
428
-
429
366
  /*
430
367
  * Iterate through children of a target element looking for span tags
431
368
  * and return the text content of the span tags, separated by spaces
432
369
  * @param {Element} target - element to check
433
370
  * @returns {string} text content of span tags
434
371
  */
435
- export function getNestedSpanText(target: Element): string {
436
- let text = ''
372
+ export function getNestedSpanText(target) {
373
+ let text = '';
437
374
  if (target && target.childNodes && target.childNodes.length) {
438
375
  each(target.childNodes, function (child) {
439
376
  if (child && child.tagName?.toLowerCase() === 'span') {
440
377
  try {
441
- const spanText = getSafeText(child)
442
- text = `${text} ${spanText}`.trim()
443
-
378
+ const spanText = getSafeText(child);
379
+ text = `${text} ${spanText}`.trim();
444
380
  if (child.childNodes && child.childNodes.length) {
445
- text = `${text} ${getNestedSpanText(child)}`.trim()
381
+ text = `${text} ${getNestedSpanText(child)}`.trim();
446
382
  }
447
- } catch (e) {
448
- logger.error('[AutoCapture]', e)
383
+ }
384
+ catch (e) {
385
+ logger.error('[AutoCapture]', e);
449
386
  }
450
387
  }
451
- })
388
+ });
452
389
  }
453
- return text
390
+ return text;
454
391
  }
455
-
456
392
  /*
457
393
  Back in the day storing events in Postgres we use Elements for autocapture events.
458
394
  Now we're using elements_chain. We used to do this parsing/processing during ingestion.
459
395
  This code is just copied over from ingestion, but we should optimize it
460
396
  to create elements_chain string directly.
461
397
  */
462
- export function getElementsChainString(elements: Properties[]): string {
463
- return elementsToString(extractElements(elements))
464
- }
465
-
466
- // This interface is called 'Element' in plugin-scaffold https://github.com/PostHog/plugin-scaffold/blob/b07d3b879796ecc7e22deb71bf627694ba05386b/src/types.ts#L200
467
- // However 'Element' is a DOM Element when run in the browser, so we have to rename it
468
- interface PHElement {
469
- text?: string
470
- tag_name?: string
471
- href?: string
472
- attr_id?: string
473
- attr_class?: string[]
474
- nth_child?: number
475
- nth_of_type?: number
476
- attributes?: Record<string, any>
477
- event_id?: number
478
- order?: number
479
- group_id?: number
398
+ export function getElementsChainString(elements) {
399
+ return elementsToString(extractElements(elements));
480
400
  }
481
-
482
- function escapeQuotes(input: string): string {
483
- return input.replace(/"|\\"/g, '\\"')
401
+ function escapeQuotes(input) {
402
+ return input.replace(/"|\\"/g, '\\"');
484
403
  }
485
-
486
- function elementsToString(elements: PHElement[]): string {
404
+ function elementsToString(elements) {
487
405
  const ret = elements.map((element) => {
488
- let el_string = ''
406
+ let el_string = '';
489
407
  if (element.tag_name) {
490
- el_string += element.tag_name
408
+ el_string += element.tag_name;
491
409
  }
492
410
  if (element.attr_class) {
493
- element.attr_class.sort()
411
+ element.attr_class.sort();
494
412
  for (const single_class of element.attr_class) {
495
- el_string += `.${single_class.replace(/"/g, '')}`
413
+ el_string += `.${single_class.replace(/"/g, '')}`;
496
414
  }
497
415
  }
498
- const attributes: Record<string, any> = {
416
+ const attributes = {
499
417
  ...(element.text ? { text: element.text } : {}),
500
418
  'nth-child': element.nth_child ?? 0,
501
419
  'nth-of-type': element.nth_of_type ?? 0,
502
420
  ...(element.href ? { href: element.href } : {}),
503
421
  ...(element.attr_id ? { attr_id: element.attr_id } : {}),
504
422
  ...element.attributes,
505
- }
506
- const sortedAttributes: Record<string, any> = {}
423
+ };
424
+ const sortedAttributes = {};
507
425
  entries(attributes)
508
426
  .sort(([a], [b]) => a.localeCompare(b))
509
- .forEach(
510
- ([key, value]) => (sortedAttributes[escapeQuotes(key.toString())] = escapeQuotes(value.toString()))
511
- )
512
- el_string += ':'
427
+ .forEach(([key, value]) => (sortedAttributes[escapeQuotes(key.toString())] = escapeQuotes(value.toString())));
428
+ el_string += ':';
513
429
  el_string += entries(sortedAttributes)
514
430
  .map(([key, value]) => `${key}="${value}"`)
515
- .join('')
516
- return el_string
517
- })
518
- return ret.join(';')
431
+ .join('');
432
+ return el_string;
433
+ });
434
+ return ret.join(';');
519
435
  }
520
-
521
- function extractElements(elements: Properties[]): PHElement[] {
436
+ function extractElements(elements) {
522
437
  return elements.map((el) => {
523
438
  const response = {
524
439
  text: el['$el_text']?.slice(0, 400),
@@ -528,23 +443,24 @@ function extractElements(elements: Properties[]): PHElement[] {
528
443
  attr_id: el['attr__id'],
529
444
  nth_child: el['nth_child'],
530
445
  nth_of_type: el['nth_of_type'],
531
- attributes: {} as { [id: string]: any },
532
- }
533
-
446
+ attributes: {},
447
+ };
534
448
  entries(el)
535
449
  .filter(([key]) => key.indexOf('attr__') === 0)
536
- .forEach(([key, value]) => (response.attributes[key] = value))
537
- return response
538
- })
450
+ .forEach(([key, value]) => (response.attributes[key] = value));
451
+ return response;
452
+ });
539
453
  }
540
-
541
- function extractAttrClass(el: Properties): PHElement['attr_class'] {
542
- const attr_class = el['attr__class']
454
+ function extractAttrClass(el) {
455
+ const attr_class = el['attr__class'];
543
456
  if (!attr_class) {
544
- return undefined
545
- } else if (isArray(attr_class)) {
546
- return attr_class
547
- } else {
548
- return splitClassString(attr_class)
457
+ return undefined;
458
+ }
459
+ else if (isArray(attr_class)) {
460
+ return attr_class;
461
+ }
462
+ else {
463
+ return splitClassString(attr_class);
549
464
  }
550
465
  }
466
+ //# sourceMappingURL=autocapture-utils.js.map