@leanbase.com/js 0.1.3 → 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 (137) hide show
  1. package/LICENSE +37 -0
  2. package/dist/autocapture-utils.d.ts +17 -0
  3. package/dist/autocapture.d.ts +35 -0
  4. package/dist/config.d.ts +5 -0
  5. package/dist/constants.d.ts +54 -0
  6. package/dist/entrypoints/main.cjs.d.ts +4 -0
  7. package/dist/entrypoints/module.es.d.ts +4 -0
  8. package/dist/extensions/rageclick.d.ts +9 -0
  9. package/dist/iife.d.ts +19 -0
  10. package/dist/index.d.ts +2 -779
  11. package/dist/leanbase-logger.d.ts +6 -0
  12. package/dist/leanbase-persistence.d.ts +64 -0
  13. package/dist/leanbase.d.ts +49 -0
  14. package/dist/leanbase.iife.js +1 -4747
  15. package/dist/leanbase.iife.js.map +1 -1
  16. package/dist/main.js +2 -0
  17. package/dist/main.js.map +1 -0
  18. package/dist/module.d.ts +780 -0
  19. package/dist/module.js +2 -0
  20. package/dist/module.js.map +1 -0
  21. package/dist/page-view.d.ts +29 -0
  22. package/dist/scroll-manager.d.ts +21 -0
  23. package/dist/session-props.d.ts +32 -0
  24. package/dist/sessionid.d.ts +50 -0
  25. package/dist/storage.d.ts +24 -0
  26. package/{src/types.ts → dist/types.d.ts} +145 -235
  27. package/dist/utils/blocked-uas.d.ts +17 -0
  28. package/dist/utils/element-utils.d.ts +5 -0
  29. package/dist/utils/event-utils.d.ts +22 -0
  30. package/dist/utils/index.d.ts +44 -0
  31. package/dist/utils/request-utils.d.ts +12 -0
  32. package/dist/utils/simple-event-emitter.d.ts +6 -0
  33. package/dist/utils/user-agent-utils.d.ts +18 -0
  34. package/dist/uuidv7.d.ts +43 -0
  35. package/dist/version.d.ts +1 -0
  36. package/lib/autocapture-utils.d.ts +17 -0
  37. package/{src/autocapture-utils.ts → lib/autocapture-utils.js} +196 -280
  38. package/lib/autocapture-utils.js.map +1 -0
  39. package/lib/autocapture.d.ts +35 -0
  40. package/lib/autocapture.js +311 -0
  41. package/lib/autocapture.js.map +1 -0
  42. package/lib/config.d.ts +5 -0
  43. package/lib/config.js +7 -0
  44. package/lib/config.js.map +1 -0
  45. package/lib/constants.d.ts +54 -0
  46. package/{src/constants.ts → lib/constants.js} +50 -57
  47. package/lib/constants.js.map +1 -0
  48. package/lib/entrypoints/main.cjs.d.ts +4 -0
  49. package/lib/entrypoints/main.cjs.js +3 -0
  50. package/lib/entrypoints/main.cjs.js.map +1 -0
  51. package/lib/entrypoints/module.es.d.ts +4 -0
  52. package/lib/entrypoints/module.es.js +3 -0
  53. package/lib/entrypoints/module.es.js.map +1 -0
  54. package/lib/extensions/rageclick.d.ts +9 -0
  55. package/lib/extensions/rageclick.js +27 -0
  56. package/lib/extensions/rageclick.js.map +1 -0
  57. package/lib/iife.d.ts +19 -0
  58. package/lib/iife.js +67 -0
  59. package/lib/iife.js.map +1 -0
  60. package/{src/index.ts → lib/index.d.ts} +2 -2
  61. package/lib/index.js +2 -0
  62. package/lib/index.js.map +1 -0
  63. package/lib/leanbase-logger.d.ts +6 -0
  64. package/lib/leanbase-logger.js +25 -0
  65. package/lib/leanbase-logger.js.map +1 -0
  66. package/lib/leanbase-persistence.d.ts +64 -0
  67. package/lib/leanbase-persistence.js +287 -0
  68. package/lib/leanbase-persistence.js.map +1 -0
  69. package/lib/leanbase.d.ts +49 -0
  70. package/lib/leanbase.js +294 -0
  71. package/lib/leanbase.js.map +1 -0
  72. package/lib/page-view.d.ts +29 -0
  73. package/lib/page-view.js +81 -0
  74. package/lib/page-view.js.map +1 -0
  75. package/lib/scroll-manager.d.ts +21 -0
  76. package/lib/scroll-manager.js +79 -0
  77. package/lib/scroll-manager.js.map +1 -0
  78. package/lib/session-props.d.ts +32 -0
  79. package/lib/session-props.js +73 -0
  80. package/lib/session-props.js.map +1 -0
  81. package/lib/sessionid.d.ts +50 -0
  82. package/{src/sessionid.ts → lib/sessionid.js} +128 -204
  83. package/lib/sessionid.js.map +1 -0
  84. package/lib/storage.d.ts +24 -0
  85. package/{src/storage.ts → lib/storage.js} +182 -225
  86. package/lib/storage.js.map +1 -0
  87. package/lib/types.d.ts +544 -0
  88. package/lib/types.js +7 -0
  89. package/lib/types.js.map +1 -0
  90. package/lib/utils/blocked-uas.d.ts +17 -0
  91. package/{src/utils/blocked-uas.ts → lib/utils/blocked-uas.js} +19 -48
  92. package/lib/utils/blocked-uas.js.map +1 -0
  93. package/lib/utils/element-utils.d.ts +5 -0
  94. package/{src/utils/element-utils.ts → lib/utils/element-utils.js} +13 -17
  95. package/lib/utils/element-utils.js.map +1 -0
  96. package/lib/utils/event-utils.d.ts +22 -0
  97. package/lib/utils/event-utils.js +258 -0
  98. package/lib/utils/event-utils.js.map +1 -0
  99. package/lib/utils/index.d.ts +44 -0
  100. package/lib/utils/index.js +183 -0
  101. package/lib/utils/index.js.map +1 -0
  102. package/lib/utils/request-utils.d.ts +12 -0
  103. package/lib/utils/request-utils.js +107 -0
  104. package/lib/utils/request-utils.js.map +1 -0
  105. package/lib/utils/simple-event-emitter.d.ts +6 -0
  106. package/lib/utils/simple-event-emitter.js +24 -0
  107. package/lib/utils/simple-event-emitter.js.map +1 -0
  108. package/lib/utils/user-agent-utils.d.ts +18 -0
  109. package/lib/utils/user-agent-utils.js +369 -0
  110. package/lib/utils/user-agent-utils.js.map +1 -0
  111. package/lib/uuidv7.d.ts +43 -0
  112. package/{src/uuidv7.ts → lib/uuidv7.js} +103 -131
  113. package/lib/uuidv7.js.map +1 -0
  114. package/lib/version.d.ts +1 -0
  115. package/lib/version.js +2 -0
  116. package/lib/version.js.map +1 -0
  117. package/package.json +56 -45
  118. package/dist/index.cjs +0 -3034
  119. package/dist/index.cjs.map +0 -1
  120. package/dist/index.mjs +0 -3032
  121. package/dist/index.mjs.map +0 -1
  122. package/src/autocapture.ts +0 -415
  123. package/src/config.ts +0 -8
  124. package/src/extensions/rageclick.ts +0 -34
  125. package/src/iife.ts +0 -87
  126. package/src/leanbase-logger.ts +0 -26
  127. package/src/leanbase-persistence.ts +0 -374
  128. package/src/leanbase.ts +0 -424
  129. package/src/page-view.ts +0 -124
  130. package/src/scroll-manager.ts +0 -103
  131. package/src/session-props.ts +0 -114
  132. package/src/utils/event-utils.ts +0 -304
  133. package/src/utils/index.ts +0 -222
  134. package/src/utils/request-utils.ts +0 -128
  135. package/src/utils/simple-event-emitter.ts +0 -27
  136. package/src/utils/user-agent-utils.ts +0 -357
  137. package/src/version.ts +0 -1
@@ -1,415 +0,0 @@
1
- import { addEventListener, each, extend } from './utils'
2
- import {
3
- autocaptureCompatibleElements,
4
- getClassNames,
5
- getDirectAndNestedSpanText,
6
- getElementsChainString,
7
- getEventTarget,
8
- getSafeText,
9
- isAngularStyleAttr,
10
- isSensitiveElement,
11
- makeSafeText,
12
- shouldCaptureDomEvent,
13
- shouldCaptureElement,
14
- shouldCaptureRageclick,
15
- shouldCaptureValue,
16
- splitClassString,
17
- } from './autocapture-utils'
18
-
19
- import RageClick from './extensions/rageclick'
20
- import { AutocaptureConfig, COPY_AUTOCAPTURE_EVENT, EventName, Properties, RemoteConfig } from './types'
21
- import { AUTOCAPTURE_DISABLED_SERVER_SIDE } from './constants'
22
-
23
- import { isBoolean, isFunction, isNull, isObject } from '@posthog/core'
24
- import { document, window } from './utils'
25
- import { convertToURL } from './utils/request-utils'
26
- import { isDocumentFragment, isElementNode, isTag, isTextNode } from './utils/element-utils'
27
- import { includes } from '@posthog/core'
28
- import { logger } from './leanbase-logger'
29
- import { Leanbase } from './leanbase'
30
-
31
- function limitText(length: number, text: string): string {
32
- if (text.length > length) {
33
- return text.slice(0, length) + '...'
34
- }
35
- return text
36
- }
37
-
38
- export function getAugmentPropertiesFromElement(elem: Element): Properties {
39
- const shouldCaptureEl = shouldCaptureElement(elem)
40
- if (!shouldCaptureEl) {
41
- return {}
42
- }
43
-
44
- const props: Properties = {}
45
-
46
- each(elem.attributes, function (attr: Attr) {
47
- if (attr.name && attr.name.indexOf('data-ph-capture-attribute') === 0) {
48
- const propertyKey = attr.name.replace('data-ph-capture-attribute-', '')
49
- const propertyValue = attr.value
50
- if (propertyKey && propertyValue && shouldCaptureValue(propertyValue)) {
51
- props[propertyKey] = propertyValue
52
- }
53
- }
54
- })
55
-
56
- return props
57
- }
58
-
59
- export function previousElementSibling(el: Element): Element | null {
60
- if (el.previousElementSibling) {
61
- return el.previousElementSibling
62
- }
63
- let _el: Element | null = el
64
- do {
65
- _el = _el.previousSibling as Element | null // resolves to ChildNode->Node, which is Element's parent class
66
- } while (_el && !isElementNode(_el))
67
- return _el
68
- }
69
-
70
- export function getDefaultProperties(eventType: string): Properties {
71
- return {
72
- $event_type: eventType,
73
- $ce_version: 1,
74
- }
75
- }
76
-
77
- export function getPropertiesFromElement(
78
- elem: Element,
79
- maskAllAttributes: boolean,
80
- maskText: boolean,
81
- elementAttributeIgnorelist: string[] | undefined
82
- ): Properties {
83
- const tag_name = elem.tagName.toLowerCase()
84
- const props: Properties = {
85
- tag_name: tag_name,
86
- }
87
- if (autocaptureCompatibleElements.indexOf(tag_name) > -1 && !maskText) {
88
- if (tag_name.toLowerCase() === 'a' || tag_name.toLowerCase() === 'button') {
89
- props['$el_text'] = limitText(1024, getDirectAndNestedSpanText(elem))
90
- } else {
91
- props['$el_text'] = limitText(1024, getSafeText(elem))
92
- }
93
- }
94
-
95
- const classes = getClassNames(elem)
96
- if (classes.length > 0)
97
- props['classes'] = classes.filter(function (c) {
98
- return c !== ''
99
- })
100
-
101
- // capture the deny list here because this not-a-class class makes it tricky to use this.config in the function below
102
- each(elem.attributes, function (attr: Attr) {
103
- // Only capture attributes we know are safe
104
- if (isSensitiveElement(elem) && ['name', 'id', 'class', 'aria-label'].indexOf(attr.name) === -1) return
105
-
106
- if (elementAttributeIgnorelist?.includes(attr.name)) return
107
-
108
- if (!maskAllAttributes && shouldCaptureValue(attr.value) && !isAngularStyleAttr(attr.name)) {
109
- let value = attr.value
110
- if (attr.name === 'class') {
111
- // html attributes can _technically_ contain linebreaks,
112
- // but we're very intolerant of them in the class string,
113
- // so we strip them.
114
- value = splitClassString(value).join(' ')
115
- }
116
- props['attr__' + attr.name] = limitText(1024, value)
117
- }
118
- })
119
-
120
- let nthChild = 1
121
- let nthOfType = 1
122
- let currentElem: Element | null = elem
123
- while ((currentElem = previousElementSibling(currentElem))) {
124
- // eslint-disable-line no-cond-assign
125
- nthChild++
126
- if (currentElem.tagName === elem.tagName) {
127
- nthOfType++
128
- }
129
- }
130
- props['nth_child'] = nthChild
131
- props['nth_of_type'] = nthOfType
132
-
133
- return props
134
- }
135
-
136
- export function autocapturePropertiesForElement(
137
- target: Element,
138
- {
139
- e,
140
- maskAllElementAttributes,
141
- maskAllText,
142
- elementAttributeIgnoreList,
143
- elementsChainAsString,
144
- }: {
145
- e: Event
146
- maskAllElementAttributes: boolean
147
- maskAllText: boolean
148
- elementAttributeIgnoreList?: string[] | undefined
149
- elementsChainAsString: boolean
150
- }
151
- ): { props: Properties; explicitNoCapture?: boolean } {
152
- const targetElementList = [target]
153
- let curEl = target
154
- while (curEl.parentNode && !isTag(curEl, 'body')) {
155
- if (isDocumentFragment(curEl.parentNode)) {
156
- targetElementList.push((curEl.parentNode as any).host)
157
- curEl = (curEl.parentNode as any).host
158
- continue
159
- }
160
- targetElementList.push(curEl.parentNode as Element)
161
- curEl = curEl.parentNode as Element
162
- }
163
-
164
- const elementsJson: Properties[] = []
165
- const autocaptureAugmentProperties: Properties = {}
166
- let href: string | false = false
167
- let explicitNoCapture = false
168
-
169
- each(targetElementList, (el) => {
170
- const shouldCaptureEl = shouldCaptureElement(el)
171
-
172
- // if the element or a parent element is an anchor tag
173
- // include the href as a property
174
- if (el.tagName.toLowerCase() === 'a') {
175
- href = el.getAttribute('href')
176
- href = shouldCaptureEl && href && shouldCaptureValue(href) && href
177
- }
178
-
179
- // allow users to programmatically prevent capturing of elements by adding class 'ph-no-capture'
180
- const classes = getClassNames(el)
181
- if (includes(classes, 'ph-no-capture')) {
182
- explicitNoCapture = true
183
- }
184
-
185
- elementsJson.push(
186
- getPropertiesFromElement(el, maskAllElementAttributes, maskAllText, elementAttributeIgnoreList)
187
- )
188
-
189
- const augmentProperties = getAugmentPropertiesFromElement(el)
190
- extend(autocaptureAugmentProperties, augmentProperties)
191
- })
192
-
193
- if (explicitNoCapture) {
194
- return { props: {}, explicitNoCapture }
195
- }
196
-
197
- if (!maskAllText) {
198
- // if the element is a button or anchor tag get the span text from any
199
- // children and include it as/with the text property on the parent element
200
- if (target.tagName.toLowerCase() === 'a' || target.tagName.toLowerCase() === 'button') {
201
- elementsJson[0]['$el_text'] = getDirectAndNestedSpanText(target)
202
- } else {
203
- elementsJson[0]['$el_text'] = getSafeText(target)
204
- }
205
- }
206
-
207
- let externalHref: string | undefined
208
- if (href) {
209
- elementsJson[0]['attr__href'] = href
210
- const hrefHost = convertToURL(href)?.host
211
- const locationHost = window?.location?.host
212
- if (hrefHost && locationHost && hrefHost !== locationHost) {
213
- externalHref = href
214
- }
215
- }
216
-
217
- const props = extend(
218
- getDefaultProperties(e.type),
219
- // Sending "$elements" is deprecated. Only one client on US cloud uses this.
220
- !elementsChainAsString ? { $elements: elementsJson } : {},
221
- // Always send $elements_chain, as it's needed downstream in site app filtering
222
- { $elements_chain: getElementsChainString(elementsJson) },
223
- elementsJson[0]?.['$el_text'] ? { $el_text: elementsJson[0]?.['$el_text'] } : {},
224
- externalHref && e.type === 'click' ? { $external_click_url: externalHref } : {},
225
- autocaptureAugmentProperties
226
- )
227
-
228
- return { props }
229
- }
230
-
231
- export class Autocapture {
232
- instance: Leanbase
233
- _initialized: boolean = false
234
- _isDisabledServerSide: boolean | null = null
235
- _elementSelectors: Set<string> | null
236
- rageclicks = new RageClick()
237
- _elementsChainAsString = false
238
-
239
- constructor(instance: Leanbase) {
240
- this.instance = instance
241
- this._elementSelectors = null
242
- }
243
-
244
- private get _config(): AutocaptureConfig {
245
- const config = isObject(this.instance.config.autocapture) ? this.instance.config.autocapture : {}
246
- // precompile the regex
247
- config.url_allowlist = config.url_allowlist?.map((url) => new RegExp(url))
248
- config.url_ignorelist = config.url_ignorelist?.map((url) => new RegExp(url))
249
- return config
250
- }
251
-
252
- _addDomEventHandlers(): void {
253
- if (!this.isBrowserSupported()) {
254
- logger.info('Disabling Automatic Event Collection because this browser is not supported')
255
- return
256
- }
257
-
258
- if (!window || !document) {
259
- return
260
- }
261
-
262
- const handler = (e: Event) => {
263
- e = e || window?.event
264
- try {
265
- this._captureEvent(e)
266
- } catch (error) {
267
- logger.error('Failed to capture event', error)
268
- }
269
- }
270
-
271
- addEventListener(document, 'submit', handler, { capture: true })
272
- addEventListener(document, 'change', handler, { capture: true })
273
- addEventListener(document, 'click', handler, { capture: true })
274
-
275
- if (this._config.capture_copied_text) {
276
- const copiedTextHandler = (e: Event) => {
277
- e = e || window?.event
278
- this._captureEvent(e, COPY_AUTOCAPTURE_EVENT)
279
- }
280
-
281
- addEventListener(document, 'copy', copiedTextHandler, { capture: true })
282
- addEventListener(document, 'cut', copiedTextHandler, { capture: true })
283
- }
284
- }
285
-
286
- public startIfEnabled() {
287
- if (this.isEnabled && !this._initialized) {
288
- this._addDomEventHandlers()
289
- this._initialized = true
290
- }
291
- }
292
-
293
- public onRemoteConfig(response: RemoteConfig) {
294
- if (response.elementsChainAsString) {
295
- this._elementsChainAsString = response.elementsChainAsString
296
- }
297
-
298
- if (this.instance.persistence) {
299
- this.instance.persistence.register({
300
- [AUTOCAPTURE_DISABLED_SERVER_SIDE]: !!response['autocapture_opt_out'],
301
- })
302
- }
303
-
304
- this._isDisabledServerSide = !!response['autocapture_opt_out']
305
- this.startIfEnabled()
306
- }
307
-
308
- public setElementSelectors(selectors: Set<string>): void {
309
- this._elementSelectors = selectors
310
- }
311
-
312
- public getElementSelectors(element: Element | null): string[] | null {
313
- const elementSelectors: string[] = []
314
-
315
- this._elementSelectors?.forEach((selector) => {
316
- const matchedElements = document?.querySelectorAll(selector)
317
- matchedElements?.forEach((matchedElement: Element) => {
318
- if (element === matchedElement) {
319
- elementSelectors.push(selector)
320
- }
321
- })
322
- })
323
-
324
- return elementSelectors
325
- }
326
-
327
- public get isEnabled(): boolean {
328
- const persistedServerDisabled = this.instance.persistence?.props[AUTOCAPTURE_DISABLED_SERVER_SIDE]
329
- const memoryDisabled = this._isDisabledServerSide
330
-
331
- if (isNull(memoryDisabled) && !isBoolean(persistedServerDisabled)) {
332
- return false
333
- }
334
-
335
- const disabledServer = this._isDisabledServerSide ?? !!persistedServerDisabled
336
- const disabledClient = !this.instance.config.autocapture
337
- return !disabledClient && !disabledServer
338
- }
339
-
340
- private _captureEvent(e: Event, eventName: EventName = '$autocapture'): boolean | void {
341
- if (!this.isEnabled) {
342
- return
343
- }
344
-
345
- /*** Don't mess with this code without running IE8 tests on it ***/
346
- let target = getEventTarget(e)
347
- if (isTextNode(target)) {
348
- // defeat Safari bug (see: http://www.quirksmode.org/js/events_properties.html)
349
- target = (target.parentNode || null) as Element | null
350
- }
351
-
352
- if (eventName === '$autocapture' && e.type === 'click' && e instanceof MouseEvent) {
353
- if (
354
- !!this.instance.config.rageclick &&
355
- this.rageclicks?.isRageClick(e.clientX, e.clientY, new Date().getTime())
356
- ) {
357
- if (shouldCaptureRageclick(target, this.instance.config.rageclick)) {
358
- this._captureEvent(e, '$rageclick')
359
- }
360
- }
361
- }
362
-
363
- const isCopyAutocapture = eventName === COPY_AUTOCAPTURE_EVENT
364
- if (
365
- target &&
366
- shouldCaptureDomEvent(
367
- target,
368
- e,
369
- this._config,
370
- // mostly this method cares about the target element, but in the case of copy events,
371
- // we want some of the work this check does without insisting on the target element's type
372
- isCopyAutocapture,
373
- // we also don't want to restrict copy checks to clicks,
374
- // so we pass that knowledge in here, rather than add the logic inside the check
375
- isCopyAutocapture ? ['copy', 'cut'] : undefined
376
- )
377
- ) {
378
- const { props, explicitNoCapture } = autocapturePropertiesForElement(target, {
379
- e,
380
- maskAllElementAttributes: this.instance.config.mask_all_element_attributes,
381
- maskAllText: this.instance.config.mask_all_text,
382
- elementAttributeIgnoreList: this._config.element_attribute_ignorelist,
383
- elementsChainAsString: this._elementsChainAsString,
384
- })
385
-
386
- if (explicitNoCapture) {
387
- return false
388
- }
389
-
390
- const elementSelectors = this.getElementSelectors(target)
391
- if (elementSelectors && elementSelectors.length > 0) {
392
- props['$element_selectors'] = elementSelectors
393
- }
394
-
395
- if (eventName === COPY_AUTOCAPTURE_EVENT) {
396
- // you can't read the data from the clipboard event,
397
- // but you can guess that you can read it from the window's current selection
398
- const selectedContent = makeSafeText(window?.getSelection()?.toString())
399
- const clipType = (e as ClipboardEvent).type || 'clipboard'
400
- if (!selectedContent) {
401
- return false
402
- }
403
- props['$selected_content'] = selectedContent
404
- props['$copy_type'] = clipType
405
- }
406
-
407
- this.instance.capture(eventName, props)
408
- return true
409
- }
410
- }
411
-
412
- isBrowserSupported(): boolean {
413
- return isFunction(document?.querySelectorAll)
414
- }
415
- }
package/src/config.ts DELETED
@@ -1,8 +0,0 @@
1
- import packageInfo from '../package.json'
2
-
3
- const Config = {
4
- DEBUG: false,
5
- LIB_VERSION: packageInfo.version,
6
- }
7
-
8
- export default Config
@@ -1,34 +0,0 @@
1
- // Naive rage click implementation: If mouse has not moved further than RAGE_CLICK_THRESHOLD_PX
2
- // over RAGE_CLICK_CLICK_COUNT clicks with max RAGE_CLICK_TIMEOUT_MS between clicks, it's
3
- // counted as a rage click
4
-
5
- const RAGE_CLICK_THRESHOLD_PX = 30
6
- const RAGE_CLICK_TIMEOUT_MS = 1000
7
- const RAGE_CLICK_CLICK_COUNT = 3
8
-
9
- export default class RageClick {
10
- clicks: { x: number; y: number; timestamp: number }[]
11
-
12
- constructor() {
13
- this.clicks = []
14
- }
15
-
16
- isRageClick(x: number, y: number, timestamp: number): boolean {
17
- const lastClick = this.clicks[this.clicks.length - 1]
18
- if (
19
- lastClick &&
20
- Math.abs(x - lastClick.x) + Math.abs(y - lastClick.y) < RAGE_CLICK_THRESHOLD_PX &&
21
- timestamp - lastClick.timestamp < RAGE_CLICK_TIMEOUT_MS
22
- ) {
23
- this.clicks.push({ x, y, timestamp })
24
-
25
- if (this.clicks.length === RAGE_CLICK_CLICK_COUNT) {
26
- return true
27
- }
28
- } else {
29
- this.clicks = [{ x, y, timestamp }]
30
- }
31
-
32
- return false
33
- }
34
- }
package/src/iife.ts DELETED
@@ -1,87 +0,0 @@
1
- import { Leanbase } from './leanbase'
2
- import type { LeanbaseConfig } from './types'
3
- import { isArray } from '@posthog/core'
4
-
5
- type QueuedCall = { fn: keyof typeof api; args: any[] }
6
-
7
- const api = {
8
- _instance: null as Leanbase | null,
9
- _queue: [] as QueuedCall[],
10
-
11
- init(apiKey: string, options?: LeanbaseConfig) {
12
- this._instance = new Leanbase(apiKey, options)
13
- const q = this._queue
14
- this._queue = []
15
- for (const { fn, args } of q) {
16
- // @ts-expect-error dynamic dispatch to API methods
17
- this[fn](...args)
18
- }
19
- },
20
-
21
- capture(...args: Parameters<NonNullable<typeof this._instance>['capture']>) {
22
- if (this._instance) {
23
- return this._instance.capture(...args)
24
- }
25
-
26
- this._queue.push({ fn: 'capture', args })
27
- },
28
-
29
- captureException(...args: Parameters<NonNullable<typeof this._instance>['captureException']>) {
30
- if (this._instance) {
31
- return this._instance.captureException(...args)
32
- }
33
-
34
- this._queue.push({ fn: 'captureException', args })
35
- },
36
-
37
- identify(...args: Parameters<NonNullable<typeof this._instance>['identify']>) {
38
- if (this._instance) {
39
- return this._instance.identify(...args)
40
- }
41
-
42
- this._queue.push({ fn: 'identify', args })
43
- },
44
-
45
- group(...args: Parameters<NonNullable<typeof this._instance>['group']>) {
46
- if (this._instance) {
47
- return this._instance.group(...args)
48
- }
49
-
50
- this._queue.push({ fn: 'group', args })
51
- },
52
-
53
- alias(...args: Parameters<NonNullable<typeof this._instance>['alias']>) {
54
- if (this._instance) {
55
- return this._instance.alias(...args)
56
- }
57
-
58
- this._queue.push({ fn: 'alias', args })
59
- },
60
-
61
- reset() {
62
- this._instance?.reset()
63
- this._instance = null
64
- },
65
-
66
- getInstance(): Leanbase | null {
67
- return this._instance
68
- },
69
- }
70
-
71
- // Attach to globalThis for browsers (SSR-safe guards inside Leanbase)
72
- ;(function attachToGlobal(g: Window & typeof globalThis & { leanbase?: typeof api; Leanbase?: typeof api }) {
73
- // Prefer not to overwrite if a stub already exists (e.g., user queued calls before script loaded)
74
- const existing = g.leanbase
75
- if (existing && typeof existing === 'object') {
76
- // If there is a pre-existing queue-compatible stub, try to drain it into our API
77
- if (isArray(existing._queue)) {
78
- api._queue = existing._queue as any
79
- }
80
- }
81
-
82
- g.leanbase = api
83
- // Also expose PascalCase alias for familiarity
84
- g.Leanbase = g.leanbase
85
- })(globalThis as Window & typeof globalThis)
86
-
87
- export default api
@@ -1,26 +0,0 @@
1
- /* eslint-disable no-console */
2
-
3
- const PREFIX = '[Leanbase]'
4
-
5
- export const logger = {
6
- info: (...args: any[]) => {
7
- if (typeof console !== 'undefined') {
8
- console.log(PREFIX, ...args)
9
- }
10
- },
11
- warn: (...args: any[]) => {
12
- if (typeof console !== 'undefined') {
13
- console.warn(PREFIX, ...args)
14
- }
15
- },
16
- error: (...args: any[]) => {
17
- if (typeof console !== 'undefined') {
18
- console.error(PREFIX, ...args)
19
- }
20
- },
21
- critical: (...args: any[]) => {
22
- if (typeof console !== 'undefined') {
23
- console.error(PREFIX, 'CRITICAL:', ...args)
24
- }
25
- },
26
- }