|
1 | 1 | import { |
| 2 | + type EntryType, |
2 | 3 | type PerformanceEntry, |
3 | 4 | PerformanceObserver, |
| 5 | + type PerformanceObserverEntryList, |
4 | 6 | performance, |
5 | 7 | } from 'node:perf_hooks'; |
6 | | -import type { Buffered, Encoder, Sink } from './sink-source.types.js'; |
| 8 | +import type { Buffered, Encoder, Observer, Sink } from './sink-source.types.js'; |
7 | 9 |
|
8 | 10 | export const DEFAULT_FLUSH_THRESHOLD = 20; |
9 | 11 |
|
10 | 12 | export type PerformanceObserverOptions<T> = { |
11 | 13 | sink: Sink<T, unknown>; |
12 | 14 | encode: (entry: PerformanceEntry) => T[]; |
13 | | - captureBuffered?: boolean; |
| 15 | + buffered?: boolean; |
14 | 16 | flushThreshold?: number; |
15 | 17 | }; |
16 | 18 |
|
17 | | -export class PerformanceObserverHandle<T> |
18 | | - implements Buffered, Encoder<PerformanceEntry, T[]> |
| 19 | +export class PerformanceObserverSink<T> |
| 20 | + implements Observer, Buffered, Encoder<PerformanceEntry, T[]> |
19 | 21 | { |
20 | 22 | #encode: (entry: PerformanceEntry) => T[]; |
21 | | - #captureBuffered: boolean; |
22 | | - #observedEntryCount: number; |
| 23 | + #buffered: boolean; |
23 | 24 | #flushThreshold: number; |
24 | 25 | #sink: Sink<T, unknown>; |
25 | 26 | #observer: PerformanceObserver | undefined; |
26 | | - #closed = false; |
| 27 | + #observedTypes: EntryType[] = ['mark', 'measure']; |
| 28 | + #getEntries = (list: PerformanceObserverEntryList) => |
| 29 | + this.#observedTypes.flatMap(t => list.getEntriesByType(t)); |
| 30 | + #observedCount: number = 0; |
27 | 31 |
|
28 | 32 | constructor(options: PerformanceObserverOptions<T>) { |
29 | 33 | this.#encode = options.encode; |
30 | 34 | this.#sink = options.sink; |
31 | | - this.#captureBuffered = options.captureBuffered ?? false; |
| 35 | + this.#buffered = options.buffered ?? false; |
32 | 36 | this.#flushThreshold = options.flushThreshold ?? DEFAULT_FLUSH_THRESHOLD; |
33 | | - this.#observedEntryCount = 0; |
34 | 37 | } |
35 | 38 |
|
36 | 39 | encode(entry: PerformanceEntry): T[] { |
37 | 40 | return this.#encode(entry); |
38 | 41 | } |
39 | 42 |
|
40 | | - connect(): void { |
41 | | - if (this.#observer || this.#closed) { |
| 43 | + subscribe(): void { |
| 44 | + if (this.#observer) { |
42 | 45 | return; |
43 | 46 | } |
44 | | - this.#observer = new PerformanceObserver(() => { |
45 | | - this.#observedEntryCount++; |
46 | | - if (this.#observedEntryCount >= this.#flushThreshold) { |
| 47 | + |
| 48 | + this.#observer = new PerformanceObserver(list => { |
| 49 | + const entries = this.#getEntries(list); |
| 50 | + this.#observedCount += entries.length; |
| 51 | + if (this.#observedCount >= this.#flushThreshold) { |
47 | 52 | this.flush(); |
48 | | - this.#observedEntryCount = 0; |
49 | 53 | } |
50 | 54 | }); |
51 | 55 |
|
52 | 56 | this.#observer.observe({ |
53 | | - entryTypes: ['mark', 'measure'], |
54 | | - buffered: this.#captureBuffered, |
| 57 | + entryTypes: this.#observedTypes, |
| 58 | + buffered: this.#buffered, |
55 | 59 | }); |
56 | 60 | } |
57 | 61 |
|
58 | | - flush(clear = false): void { |
59 | | - if (this.#closed || !this.#sink) { |
| 62 | + flush(): void { |
| 63 | + if (!this.#observer) { |
60 | 64 | return; |
61 | 65 | } |
62 | | - const entries = [ |
63 | | - ...performance.getEntriesByType('mark'), |
64 | | - ...performance.getEntriesByType('measure'), |
65 | | - ]; |
66 | 66 |
|
67 | | - // Process all entries |
68 | | - entries |
69 | | - .filter(e => e.entryType === 'mark' || e.entryType === 'measure') |
70 | | - .forEach(e => { |
71 | | - const encoded = this.encode(e); |
72 | | - encoded.forEach(item => { |
73 | | - this.#sink.write(item); |
74 | | - }); |
75 | | - |
76 | | - if (clear) { |
77 | | - if (e.entryType === 'mark') { |
78 | | - performance.clearMarks(e.name); |
79 | | - } |
80 | | - if (e.entryType === 'measure') { |
81 | | - performance.clearMeasures(e.name); |
82 | | - } |
83 | | - } |
| 67 | + const entries = this.#getEntries(performance); |
| 68 | + entries.forEach(entry => { |
| 69 | + const encoded = this.encode(entry); |
| 70 | + encoded.forEach(item => { |
| 71 | + this.#sink.write(item); |
84 | 72 | }); |
| 73 | + }); |
| 74 | + this.#observedCount = 0; |
85 | 75 | } |
86 | 76 |
|
87 | | - disconnect(): void { |
| 77 | + unsubscribe(): void { |
88 | 78 | if (!this.#observer) { |
89 | 79 | return; |
90 | 80 | } |
91 | 81 | this.#observer?.disconnect(); |
92 | 82 | this.#observer = undefined; |
93 | 83 | } |
94 | 84 |
|
95 | | - close(): void { |
96 | | - if (this.#closed) { |
97 | | - return; |
98 | | - } |
99 | | - this.flush(); |
100 | | - this.#closed = true; |
101 | | - this.disconnect(); |
102 | | - } |
103 | | - |
104 | | - isConnected(): boolean { |
105 | | - return this.#observer !== undefined && !this.#closed; |
| 85 | + isSubscribed(): boolean { |
| 86 | + return this.#observer !== undefined; |
106 | 87 | } |
107 | 88 | } |
0 commit comments