@leanbase-giangnd/js 0.0.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 (49) hide show
  1. package/README.md +143 -0
  2. package/dist/index.cjs +6012 -0
  3. package/dist/index.cjs.map +1 -0
  4. package/dist/index.d.ts +1484 -0
  5. package/dist/index.mjs +6010 -0
  6. package/dist/index.mjs.map +1 -0
  7. package/dist/leanbase.iife.js +13431 -0
  8. package/dist/leanbase.iife.js.map +1 -0
  9. package/package.json +48 -0
  10. package/src/autocapture-utils.ts +550 -0
  11. package/src/autocapture.ts +415 -0
  12. package/src/config.ts +8 -0
  13. package/src/constants.ts +108 -0
  14. package/src/extensions/rageclick.ts +34 -0
  15. package/src/extensions/replay/external/config.ts +278 -0
  16. package/src/extensions/replay/external/denylist.ts +32 -0
  17. package/src/extensions/replay/external/lazy-loaded-session-recorder.ts +1376 -0
  18. package/src/extensions/replay/external/mutation-throttler.ts +109 -0
  19. package/src/extensions/replay/external/network-plugin.ts +701 -0
  20. package/src/extensions/replay/external/sessionrecording-utils.ts +141 -0
  21. package/src/extensions/replay/external/triggerMatching.ts +422 -0
  22. package/src/extensions/replay/rrweb-plugins/patch.ts +39 -0
  23. package/src/extensions/replay/session-recording.ts +285 -0
  24. package/src/extensions/replay/types/rrweb-types.ts +575 -0
  25. package/src/extensions/replay/types/rrweb.ts +114 -0
  26. package/src/extensions/sampling.ts +26 -0
  27. package/src/iife.ts +87 -0
  28. package/src/index.ts +2 -0
  29. package/src/leanbase-logger.ts +26 -0
  30. package/src/leanbase-persistence.ts +374 -0
  31. package/src/leanbase.ts +457 -0
  32. package/src/page-view.ts +124 -0
  33. package/src/scroll-manager.ts +103 -0
  34. package/src/session-props.ts +114 -0
  35. package/src/sessionid.ts +330 -0
  36. package/src/storage.ts +410 -0
  37. package/src/types/fflate.d.ts +5 -0
  38. package/src/types/rrweb-record.d.ts +8 -0
  39. package/src/types.ts +807 -0
  40. package/src/utils/blocked-uas.ts +162 -0
  41. package/src/utils/element-utils.ts +50 -0
  42. package/src/utils/event-utils.ts +304 -0
  43. package/src/utils/index.ts +222 -0
  44. package/src/utils/logger.ts +26 -0
  45. package/src/utils/request-utils.ts +128 -0
  46. package/src/utils/simple-event-emitter.ts +27 -0
  47. package/src/utils/user-agent-utils.ts +357 -0
  48. package/src/uuidv7.ts +268 -0
  49. package/src/version.ts +1 -0
@@ -0,0 +1,109 @@
1
+ import type { eventWithTime, mutationCallbackParam } from '../types/rrweb-types'
2
+ import { INCREMENTAL_SNAPSHOT_EVENT_TYPE, MUTATION_SOURCE_TYPE } from './sessionrecording-utils'
3
+ import type { rrwebRecord } from '../types/rrweb'
4
+ import { BucketedRateLimiter } from '@posthog/core'
5
+ import { logger } from '../../../utils/logger'
6
+
7
+ export class MutationThrottler {
8
+ private _loggedTracker: Record<string, boolean> = {}
9
+ private _rateLimiter: BucketedRateLimiter<number>
10
+
11
+ constructor(
12
+ private readonly _rrweb: rrwebRecord,
13
+ private readonly _options: {
14
+ bucketSize?: number
15
+ refillRate?: number
16
+ onBlockedNode?: (id: number, node: Node | null) => void
17
+ } = {}
18
+ ) {
19
+ const configuredBucketSize = this._options.bucketSize ?? 100
20
+ const effectiveBucketSize = Math.max(configuredBucketSize - 1, 1)
21
+
22
+ this._rateLimiter = new BucketedRateLimiter({
23
+ bucketSize: effectiveBucketSize,
24
+ refillRate: this._options.refillRate ?? 10,
25
+ refillInterval: 1000, // one second
26
+ _onBucketRateLimited: this._onNodeRateLimited,
27
+ _logger: logger,
28
+ })
29
+ }
30
+
31
+ private _onNodeRateLimited = (key: number) => {
32
+ if (!this._loggedTracker[key]) {
33
+ this._loggedTracker[key] = true
34
+ const node = this._getNode(key)
35
+ this._options.onBlockedNode?.(key, node)
36
+ }
37
+ }
38
+
39
+ private _getNodeOrRelevantParent = (id: number): [number, Node | null] => {
40
+ // For some nodes we know they are part of a larger tree such as an SVG.
41
+ // For those we want to block the entire node, not just the specific attribute
42
+
43
+ const node = this._getNode(id)
44
+
45
+ // Check if the node is an Element and then find the closest parent that is an SVG
46
+ if (node?.nodeName !== 'svg' && node instanceof Element) {
47
+ const closestSVG = node.closest('svg')
48
+
49
+ if (closestSVG) {
50
+ return [this._rrweb.mirror.getId(closestSVG), closestSVG]
51
+ }
52
+ }
53
+
54
+ return [id, node]
55
+ }
56
+
57
+ private _getNode = (id: number) => this._rrweb.mirror.getNode(id)
58
+
59
+ private _numberOfChanges = (data: Partial<mutationCallbackParam>) => {
60
+ return (
61
+ (data.removes?.length ?? 0) +
62
+ (data.attributes?.length ?? 0) +
63
+ (data.texts?.length ?? 0) +
64
+ (data.adds?.length ?? 0)
65
+ )
66
+ }
67
+
68
+ public throttleMutations = (event: eventWithTime) => {
69
+ if (event.type !== INCREMENTAL_SNAPSHOT_EVENT_TYPE || event.data.source !== MUTATION_SOURCE_TYPE) {
70
+ return event
71
+ }
72
+
73
+ const data = event.data as Partial<mutationCallbackParam>
74
+ const initialMutationCount = this._numberOfChanges(data)
75
+
76
+ if (data.attributes) {
77
+ // Most problematic mutations come from attrs where the style or minor properties are changed rapidly
78
+ data.attributes = data.attributes.filter((attr) => {
79
+ const [nodeId] = this._getNodeOrRelevantParent(attr.id)
80
+
81
+ const isRateLimited = this._rateLimiter.consumeRateLimit(nodeId)
82
+
83
+ if (isRateLimited) {
84
+ return false
85
+ }
86
+
87
+ return attr
88
+ })
89
+ }
90
+
91
+ // Check if every part of the mutation is empty in which case there is nothing to do
92
+ const mutationCount = this._numberOfChanges(data)
93
+
94
+ if (mutationCount === 0 && initialMutationCount !== mutationCount) {
95
+ // If we have modified the mutation count and the remaining count is 0, then we don't need the event.
96
+ return
97
+ }
98
+ return event
99
+ }
100
+
101
+ public reset() {
102
+ this._loggedTracker = {}
103
+ }
104
+
105
+ public stop() {
106
+ this._rateLimiter.stop()
107
+ this.reset()
108
+ }
109
+ }