Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 0 additions & 17 deletions packages/rum/src/domain/record/eventIds.ts

This file was deleted.

4 changes: 0 additions & 4 deletions packages/rum/src/domain/record/internalApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@ import type { ShadowRootsController } from './shadowRootsController'
import type { RecordingScope } from './recordingScope'
import { createRecordingScope } from './recordingScope'
import { createElementsScrollPositions } from './elementsScrollPositions'
import { createEventIds } from './eventIds'
import { createNodeIds } from './nodeIds'
import type { EmitRecordCallback } from './record.types'
import type { SerializationTransaction } from './serialization'
import { SerializationKind, serializeInTransaction, serializeNode } from './serialization'
Expand Down Expand Up @@ -76,8 +74,6 @@ function createTemporaryRecordingScope(configuration?: Partial<RumConfiguration>
...configuration,
} as RumConfiguration,
createElementsScrollPositions(),
createEventIds(),
createNodeIds(),
{
addShadowRoot: noop,
removeShadowRoot: noop,
Expand Down
126 changes: 126 additions & 0 deletions packages/rum/src/domain/record/itemIds.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
import type { EventId, ItemIds, NodeId, StringId, StyleSheetId } from './itemIds'
import {
createEventIds,
createNodeIds,
createStringIds,
createStyleSheetIds,
EventIdConstants,
NodeIdConstants,
StringIdConstants,
StyleSheetIdConstants,
} from './itemIds'

describe('ItemIds', () => {
const describeItemIdVariant = <ItemType, ItemId extends number>(
name: string,
createIdMap: () => ItemIds<ItemType, ItemId>,
createItem: () => ItemType,
firstId: ItemId
) => {
describe(name, () => {
let itemIds = createIdMap()

beforeEach(() => {
itemIds = createIdMap()
})

describe('clear', () => {
it('removes all id mappings', () => {
const item = createItem()
itemIds.getOrInsert(item)
expect(itemIds.get(item)).toBe(firstId)

itemIds.clear()
expect(itemIds.get(item)).toBeUndefined()
})

it('restarts the id sequence', () => {
for (let id = firstId; id < firstId + 3; id++) {
const item = createItem()
expect(itemIds.getOrInsert(item)).toBe(id)
expect(itemIds.getOrInsert(item)).toBe(id)
}

itemIds.clear()

for (let id = firstId; id < firstId + 3; id++) {
const item = createItem()
expect(itemIds.getOrInsert(item)).toBe(id)
expect(itemIds.getOrInsert(item)).toBe(id)
}
})
})

describe('get', () => {
it('returns undefined for items that have not been assigned an id', () => {
expect(itemIds.get(createItem())).toBe(undefined)
})

it('returns the assigned id if one exists', () => {
const item = createItem()
itemIds.getOrInsert(item)
expect(itemIds.get(item)).toBe(firstId)
})
})

describe('getOrInsert', () => {
it('assigns ids in order', () => {
for (let id = firstId; id < firstId + 3; id++) {
const item = createItem()
expect(itemIds.getOrInsert(item)).toBe(id)
expect(itemIds.getOrInsert(item)).toBe(id)
}
})

it('reuses any existing id', () => {
itemIds.getOrInsert(createItem())
itemIds.getOrInsert(createItem())
const item = createItem()
const itemId = itemIds.getOrInsert(item)
expect(itemIds.getOrInsert(item)).toBe(itemId)
expect(itemIds.get(item)).toBe(itemId)
})
})

describe('size', () => {
it('increments when an id is assigned', () => {
expect(itemIds.size).toBe(0)
itemIds.getOrInsert(createItem())
expect(itemIds.size).toBe(1)
itemIds.getOrInsert(createItem())
expect(itemIds.size).toBe(2)
})
})
})
}

describeItemIdVariant(
'EventIds',
createEventIds,
() => new Event('someCustomEvent'),
EventIdConstants.FIRST_ID as EventId
)

describeItemIdVariant(
'NodeIds',
createNodeIds,
() => document.createElement('div'),
NodeIdConstants.FIRST_ID as NodeId
)

let nextString = 0
describeItemIdVariant(
'StringIds',
createStringIds,
() => `string${nextString++}`,
StringIdConstants.FIRST_ID as StringId
)

describeItemIdVariant(
'StyleSheetIds',
createStyleSheetIds,
// The CSSStyleSheet constructor is not available on older browsers.
() => ({ type: 'CSSStyleSheet' }),
StyleSheetIdConstants.FIRST_ID as StyleSheetId
)
})
85 changes: 85 additions & 0 deletions packages/rum/src/domain/record/itemIds.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
export type EventId = number & { __brand: 'EventId' }
export type EventIds = ItemIds<Event, EventId>
export const enum EventIdConstants {
FIRST_ID = 1,
}
export function createEventIds(): EventIds {
return createWeakIdMap(EventIdConstants.FIRST_ID as EventId)
}

export type NodeId = number & { __brand: 'NodeId' }
export type NodeIds = ItemIds<Node, NodeId>
export const enum NodeIdConstants {
FIRST_ID = 0,
}
export function createNodeIds(): NodeIds {
return createWeakIdMap(NodeIdConstants.FIRST_ID as NodeId)
}

export type StringId = number & { __brand: 'StringId' }
export type StringIds = ItemIds<string, StringId>
export const enum StringIdConstants {
FIRST_ID = 0,
}
export function createStringIds(): StringIds {
return createIdMap(StringIdConstants.FIRST_ID as StringId)
}

export type StyleSheetId = number & { __brand: 'StyleSheetId' }
export type StyleSheetIds = ItemIds<CSSStyleSheet, StyleSheetId>
export const enum StyleSheetIdConstants {
FIRST_ID = 0,
}
export function createStyleSheetIds(): StyleSheetIds {
return createWeakIdMap(StyleSheetIdConstants.FIRST_ID as StyleSheetId)
}

export interface ItemIds<ItemType, ItemId extends number> {
clear(this: void): void
get(this: void, item: ItemType): ItemId | undefined
getOrInsert(this: void, item: ItemType): ItemId
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This method was called assign() for the old NodeIds and getIdForEvent() for the old EventIds, but it had the same behavior in both cases. I've renamed it to use the same name as the standard Map method that does essentially the same thing. I think this name makes the behavior a bit clearer than either of the previous names.

get size(): number
}

function createIdMap<ItemType, ItemId extends number>(firstId: ItemId): ItemIds<ItemType, ItemId> {
return createItemIds(() => new Map<ItemType, ItemId>(), firstId)
}

function createWeakIdMap<ItemType extends object, ItemId extends number>(firstId: ItemId): ItemIds<ItemType, ItemId> {
return createItemIds(() => new WeakMap<ItemType, ItemId>(), firstId)
}
Comment on lines +44 to +50
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need two variations here because strings cannot be stored in a WeakMap, since they're value types.


interface MapLike<Key, Value> {
get(key: Key): Value | undefined
set(key: Key, value: Value): void
}

function createItemIds<ItemType, ItemId extends number>(
createMap: () => MapLike<ItemType, ItemId>,
firstId: ItemId
): ItemIds<ItemType, ItemId> {
let map = createMap()
let nextId = firstId

const get = (object: ItemType): ItemId | undefined => map.get(object)

return {
clear(): void {
map = createMap()
nextId = firstId
},
get,
getOrInsert(object: ItemType): ItemId {
// Try to reuse any existing id.
let id = get(object)
if (id === undefined) {
id = nextId++ as ItemId
map.set(object, id)
}
return id
},
get size(): number {
return nextId - firstId
},
}
}
98 changes: 0 additions & 98 deletions packages/rum/src/domain/record/nodeIds.spec.ts

This file was deleted.

46 changes: 0 additions & 46 deletions packages/rum/src/domain/record/nodeIds.ts

This file was deleted.

Loading