Skip to content

Commit cb8a95e

Browse files
🎨 [PANA-5260] Consolidate recorder object id tracking code (#4049)
1 parent 99d6a06 commit cb8a95e

17 files changed

+309
-193
lines changed

‎packages/rum/src/domain/record/eventIds.ts‎

Lines changed: 0 additions & 17 deletions
This file was deleted.

‎packages/rum/src/domain/record/internalApi.ts‎

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,6 @@ import type { ShadowRootsController } from './shadowRootsController'
77
import type { RecordingScope } from './recordingScope'
88
import { createRecordingScope } from './recordingScope'
99
import { createElementsScrollPositions } from './elementsScrollPositions'
10-
import { createEventIds } from './eventIds'
11-
import { createNodeIds } from './nodeIds'
1210
import type { EmitRecordCallback } from './record.types'
1311
import type { SerializationTransaction } from './serialization'
1412
import { SerializationKind, serializeInTransaction, serializeNode } from './serialization'
@@ -76,8 +74,6 @@ function createTemporaryRecordingScope(configuration?: Partial<RumConfiguration>
7674
...configuration,
7775
} as RumConfiguration,
7876
createElementsScrollPositions(),
79-
createEventIds(),
80-
createNodeIds(),
8177
{
8278
addShadowRoot: noop,
8379
removeShadowRoot: noop,
Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
import type { EventId, ItemIds, NodeId, StringId, StyleSheetId } from './itemIds'
2+
import {
3+
createEventIds,
4+
createNodeIds,
5+
createStringIds,
6+
createStyleSheetIds,
7+
EventIdConstants,
8+
NodeIdConstants,
9+
StringIdConstants,
10+
StyleSheetIdConstants,
11+
} from './itemIds'
12+
13+
describe('ItemIds', () => {
14+
const describeItemIdVariant = <ItemType, ItemId extends number>(
15+
name: string,
16+
createIdMap: () => ItemIds<ItemType, ItemId>,
17+
createItem: () => ItemType,
18+
firstId: ItemId
19+
) => {
20+
describe(name, () => {
21+
let itemIds = createIdMap()
22+
23+
beforeEach(() => {
24+
itemIds = createIdMap()
25+
})
26+
27+
describe('clear', () => {
28+
it('removes all id mappings', () => {
29+
const item = createItem()
30+
itemIds.getOrInsert(item)
31+
expect(itemIds.get(item)).toBe(firstId)
32+
33+
itemIds.clear()
34+
expect(itemIds.get(item)).toBeUndefined()
35+
})
36+
37+
it('restarts the id sequence', () => {
38+
for (let id = firstId; id < firstId + 3; id++) {
39+
const item = createItem()
40+
expect(itemIds.getOrInsert(item)).toBe(id)
41+
expect(itemIds.getOrInsert(item)).toBe(id)
42+
}
43+
44+
itemIds.clear()
45+
46+
for (let id = firstId; id < firstId + 3; id++) {
47+
const item = createItem()
48+
expect(itemIds.getOrInsert(item)).toBe(id)
49+
expect(itemIds.getOrInsert(item)).toBe(id)
50+
}
51+
})
52+
})
53+
54+
describe('get', () => {
55+
it('returns undefined for items that have not been assigned an id', () => {
56+
expect(itemIds.get(createItem())).toBe(undefined)
57+
})
58+
59+
it('returns the assigned id if one exists', () => {
60+
const item = createItem()
61+
itemIds.getOrInsert(item)
62+
expect(itemIds.get(item)).toBe(firstId)
63+
})
64+
})
65+
66+
describe('getOrInsert', () => {
67+
it('assigns ids in order', () => {
68+
for (let id = firstId; id < firstId + 3; id++) {
69+
const item = createItem()
70+
expect(itemIds.getOrInsert(item)).toBe(id)
71+
expect(itemIds.getOrInsert(item)).toBe(id)
72+
}
73+
})
74+
75+
it('reuses any existing id', () => {
76+
itemIds.getOrInsert(createItem())
77+
itemIds.getOrInsert(createItem())
78+
const item = createItem()
79+
const itemId = itemIds.getOrInsert(item)
80+
expect(itemIds.getOrInsert(item)).toBe(itemId)
81+
expect(itemIds.get(item)).toBe(itemId)
82+
})
83+
})
84+
85+
describe('size', () => {
86+
it('increments when an id is assigned', () => {
87+
expect(itemIds.size).toBe(0)
88+
itemIds.getOrInsert(createItem())
89+
expect(itemIds.size).toBe(1)
90+
itemIds.getOrInsert(createItem())
91+
expect(itemIds.size).toBe(2)
92+
})
93+
})
94+
})
95+
}
96+
97+
describeItemIdVariant(
98+
'EventIds',
99+
createEventIds,
100+
() => new Event('someCustomEvent'),
101+
EventIdConstants.FIRST_ID as EventId
102+
)
103+
104+
describeItemIdVariant(
105+
'NodeIds',
106+
createNodeIds,
107+
() => document.createElement('div'),
108+
NodeIdConstants.FIRST_ID as NodeId
109+
)
110+
111+
let nextString = 0
112+
describeItemIdVariant(
113+
'StringIds',
114+
createStringIds,
115+
() => `string${nextString++}`,
116+
StringIdConstants.FIRST_ID as StringId
117+
)
118+
119+
describeItemIdVariant(
120+
'StyleSheetIds',
121+
createStyleSheetIds,
122+
// The CSSStyleSheet constructor is not available on older browsers.
123+
() => ({ type: 'CSSStyleSheet' }),
124+
StyleSheetIdConstants.FIRST_ID as StyleSheetId
125+
)
126+
})
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
export type EventId = number & { __brand: 'EventId' }
2+
export type EventIds = ItemIds<Event, EventId>
3+
export const enum EventIdConstants {
4+
FIRST_ID = 1,
5+
}
6+
export function createEventIds(): EventIds {
7+
return createWeakIdMap(EventIdConstants.FIRST_ID as EventId)
8+
}
9+
10+
export type NodeId = number & { __brand: 'NodeId' }
11+
export type NodeIds = ItemIds<Node, NodeId>
12+
export const enum NodeIdConstants {
13+
FIRST_ID = 0,
14+
}
15+
export function createNodeIds(): NodeIds {
16+
return createWeakIdMap(NodeIdConstants.FIRST_ID as NodeId)
17+
}
18+
19+
export type StringId = number & { __brand: 'StringId' }
20+
export type StringIds = ItemIds<string, StringId>
21+
export const enum StringIdConstants {
22+
FIRST_ID = 0,
23+
}
24+
export function createStringIds(): StringIds {
25+
return createIdMap(StringIdConstants.FIRST_ID as StringId)
26+
}
27+
28+
export type StyleSheetId = number & { __brand: 'StyleSheetId' }
29+
export type StyleSheetIds = ItemIds<CSSStyleSheet, StyleSheetId>
30+
export const enum StyleSheetIdConstants {
31+
FIRST_ID = 0,
32+
}
33+
export function createStyleSheetIds(): StyleSheetIds {
34+
return createWeakIdMap(StyleSheetIdConstants.FIRST_ID as StyleSheetId)
35+
}
36+
37+
export interface ItemIds<ItemType, ItemId extends number> {
38+
clear(this: void): void
39+
get(this: void, item: ItemType): ItemId | undefined
40+
getOrInsert(this: void, item: ItemType): ItemId
41+
get size(): number
42+
}
43+
44+
function createIdMap<ItemType, ItemId extends number>(firstId: ItemId): ItemIds<ItemType, ItemId> {
45+
return createItemIds(() => new Map<ItemType, ItemId>(), firstId)
46+
}
47+
48+
function createWeakIdMap<ItemType extends object, ItemId extends number>(firstId: ItemId): ItemIds<ItemType, ItemId> {
49+
return createItemIds(() => new WeakMap<ItemType, ItemId>(), firstId)
50+
}
51+
52+
interface MapLike<Key, Value> {
53+
get(key: Key): Value | undefined
54+
set(key: Key, value: Value): void
55+
}
56+
57+
function createItemIds<ItemType, ItemId extends number>(
58+
createMap: () => MapLike<ItemType, ItemId>,
59+
firstId: ItemId
60+
): ItemIds<ItemType, ItemId> {
61+
let map = createMap()
62+
let nextId = firstId
63+
64+
const get = (object: ItemType): ItemId | undefined => map.get(object)
65+
66+
return {
67+
clear(): void {
68+
map = createMap()
69+
nextId = firstId
70+
},
71+
get,
72+
getOrInsert(object: ItemType): ItemId {
73+
// Try to reuse any existing id.
74+
let id = get(object)
75+
if (id === undefined) {
76+
id = nextId++ as ItemId
77+
map.set(object, id)
78+
}
79+
return id
80+
},
81+
get size(): number {
82+
return nextId - firstId
83+
},
84+
}
85+
}

‎packages/rum/src/domain/record/nodeIds.spec.ts‎

Lines changed: 0 additions & 98 deletions
This file was deleted.

‎packages/rum/src/domain/record/nodeIds.ts‎

Lines changed: 0 additions & 46 deletions
This file was deleted.

0 commit comments

Comments
 (0)