libp2p 0.40.0 → 0.41.0-19e96cc

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 (101) hide show
  1. package/README.md +26 -6
  2. package/dist/index.min.js +50 -0
  3. package/dist/src/address-manager/index.d.ts.map +1 -1
  4. package/dist/src/address-manager/index.js +1 -1
  5. package/dist/src/address-manager/index.js.map +1 -1
  6. package/dist/src/config.d.ts.map +1 -1
  7. package/dist/src/config.js +0 -11
  8. package/dist/src/config.js.map +1 -1
  9. package/dist/src/connection/index.d.ts +83 -0
  10. package/dist/src/connection/index.d.ts.map +1 -0
  11. package/dist/src/connection/index.js +96 -0
  12. package/dist/src/connection/index.js.map +1 -0
  13. package/dist/src/connection-manager/dialer/index.d.ts +13 -14
  14. package/dist/src/connection-manager/dialer/index.d.ts.map +1 -1
  15. package/dist/src/connection-manager/dialer/index.js +82 -62
  16. package/dist/src/connection-manager/dialer/index.js.map +1 -1
  17. package/dist/src/connection-manager/index.d.ts +7 -115
  18. package/dist/src/connection-manager/index.d.ts.map +1 -1
  19. package/dist/src/connection-manager/index.js +21 -57
  20. package/dist/src/connection-manager/index.js.map +1 -1
  21. package/dist/src/fetch/index.d.ts +14 -0
  22. package/dist/src/fetch/index.d.ts.map +1 -1
  23. package/dist/src/fetch/index.js +29 -3
  24. package/dist/src/fetch/index.js.map +1 -1
  25. package/dist/src/get-peer.d.ts +2 -2
  26. package/dist/src/get-peer.d.ts.map +1 -1
  27. package/dist/src/get-peer.js +2 -5
  28. package/dist/src/get-peer.js.map +1 -1
  29. package/dist/src/index.d.ts +192 -77
  30. package/dist/src/index.d.ts.map +1 -1
  31. package/dist/src/index.js +41 -1
  32. package/dist/src/index.js.map +1 -1
  33. package/dist/src/insecure/index.d.ts +23 -0
  34. package/dist/src/insecure/index.d.ts.map +1 -1
  35. package/dist/src/insecure/index.js +23 -0
  36. package/dist/src/insecure/index.js.map +1 -1
  37. package/dist/src/keychain/index.d.ts +1 -1
  38. package/dist/src/keychain/index.d.ts.map +1 -1
  39. package/dist/src/libp2p.d.ts +3 -3
  40. package/dist/src/libp2p.d.ts.map +1 -1
  41. package/dist/src/libp2p.js +2 -3
  42. package/dist/src/libp2p.js.map +1 -1
  43. package/dist/src/nat-manager.d.ts.map +1 -1
  44. package/dist/src/nat-manager.js +5 -1
  45. package/dist/src/nat-manager.js.map +1 -1
  46. package/dist/src/pnet/index.d.ts +23 -0
  47. package/dist/src/pnet/index.d.ts.map +1 -1
  48. package/dist/src/pnet/index.js +23 -0
  49. package/dist/src/pnet/index.js.map +1 -1
  50. package/dist/src/pnet/key-generator.d.ts +3 -2
  51. package/dist/src/pnet/key-generator.d.ts.map +1 -1
  52. package/dist/src/pnet/key-generator.js +7 -10
  53. package/dist/src/pnet/key-generator.js.map +1 -1
  54. package/dist/src/registrar.d.ts +5 -0
  55. package/dist/src/registrar.d.ts.map +1 -1
  56. package/dist/src/registrar.js +25 -0
  57. package/dist/src/registrar.js.map +1 -1
  58. package/dist/src/transport-manager.d.ts.map +1 -1
  59. package/dist/src/transport-manager.js +1 -2
  60. package/dist/src/transport-manager.js.map +1 -1
  61. package/dist/src/upgrader.d.ts +1 -1
  62. package/dist/src/upgrader.d.ts.map +1 -1
  63. package/dist/src/upgrader.js +46 -59
  64. package/dist/src/upgrader.js.map +1 -1
  65. package/package.json +15 -14
  66. package/src/address-manager/index.ts +3 -2
  67. package/src/circuit/README.md +6 -6
  68. package/src/config.ts +0 -11
  69. package/src/connection/index.ts +171 -0
  70. package/src/connection-manager/dialer/index.ts +100 -75
  71. package/src/connection-manager/index.ts +29 -192
  72. package/src/fetch/README.md +7 -2
  73. package/src/fetch/index.ts +30 -3
  74. package/src/get-peer.ts +3 -7
  75. package/src/index.ts +224 -83
  76. package/src/insecure/index.ts +24 -0
  77. package/src/keychain/index.ts +1 -1
  78. package/src/libp2p.ts +5 -6
  79. package/src/nat-manager.ts +6 -1
  80. package/src/pnet/README.md +31 -29
  81. package/src/pnet/index.ts +24 -0
  82. package/src/pnet/key-generator.ts +7 -12
  83. package/src/registrar.ts +29 -2
  84. package/src/transport-manager.ts +1 -2
  85. package/src/upgrader.ts +51 -64
  86. package/src/version.ts +1 -1
  87. package/dist/src/metrics/index.d.ts +0 -93
  88. package/dist/src/metrics/index.d.ts.map +0 -1
  89. package/dist/src/metrics/index.js +0 -234
  90. package/dist/src/metrics/index.js.map +0 -1
  91. package/dist/src/metrics/moving-average.d.ts +0 -14
  92. package/dist/src/metrics/moving-average.d.ts.map +0 -1
  93. package/dist/src/metrics/moving-average.js +0 -40
  94. package/dist/src/metrics/moving-average.js.map +0 -1
  95. package/dist/src/metrics/stats.d.ts +0 -87
  96. package/dist/src/metrics/stats.d.ts.map +0 -1
  97. package/dist/src/metrics/stats.js +0 -183
  98. package/dist/src/metrics/stats.js.map +0 -1
  99. package/src/metrics/index.ts +0 -311
  100. package/src/metrics/moving-average.ts +0 -53
  101. package/src/metrics/stats.ts +0 -238
@@ -1,311 +0,0 @@
1
- import { pipe } from 'it-pipe'
2
- import each from 'it-foreach'
3
- import LRU from 'hashlru'
4
- import { METRICS as defaultOptions } from '../constants.js'
5
- import { DefaultStats, StatsInit } from './stats.js'
6
- import type { ComponentMetricsUpdate, Metrics, Stats, TrackedMetric, TrackStreamOptions } from '@libp2p/interface-metrics'
7
- import type { PeerId } from '@libp2p/interface-peer-id'
8
- import type { Startable } from '@libp2p/interfaces/startable'
9
-
10
- const initialCounters: ['dataReceived', 'dataSent'] = [
11
- 'dataReceived',
12
- 'dataSent'
13
- ]
14
-
15
- const directionToEvent = {
16
- in: 'dataReceived',
17
- out: 'dataSent'
18
- }
19
-
20
- export interface OnMessageOptions {
21
- remotePeer: PeerId
22
- protocol?: string
23
- direction: 'in' | 'out'
24
- dataLength: number
25
- }
26
-
27
- export interface MetricsInit {
28
- enabled: boolean
29
- computeThrottleMaxQueueSize: number
30
- computeThrottleTimeout: number
31
- movingAverageIntervals: number[]
32
- maxOldPeersRetention: number
33
- }
34
-
35
- export class DefaultMetrics implements Metrics, Startable {
36
- public globalStats: DefaultStats
37
-
38
- private readonly enabled: boolean
39
- private readonly peerStats: Map<string, DefaultStats>
40
- private readonly protocolStats: Map<string, DefaultStats>
41
- private readonly oldPeers: ReturnType<typeof LRU>
42
- private running: boolean
43
- private readonly systems: Map<string, Map<string, Map<string, TrackedMetric>>>
44
- private readonly statsInit: StatsInit
45
-
46
- constructor (init: MetricsInit) {
47
- this.enabled = init.enabled
48
- this.statsInit = {
49
- ...init,
50
- initialCounters
51
- }
52
- this.globalStats = new DefaultStats(this.statsInit)
53
- this.peerStats = new Map()
54
- this.protocolStats = new Map()
55
- this.oldPeers = LRU(init.maxOldPeersRetention ?? defaultOptions.maxOldPeersRetention)
56
- this.running = false
57
- this._onMessage = this._onMessage.bind(this)
58
- this.systems = new Map()
59
- }
60
-
61
- isStarted () {
62
- return this.running
63
- }
64
-
65
- /**
66
- * Must be called for stats to saved. Any data pushed for tracking
67
- * will be ignored.
68
- */
69
- async start () {
70
- if (!this.enabled) {
71
- return
72
- }
73
-
74
- this.running = true
75
- }
76
-
77
- /**
78
- * Stops all averages timers and prevents new data from being tracked.
79
- * Once `stop` is called, `start` must be called to resume stats tracking.
80
- */
81
- async stop () {
82
- if (!this.running) {
83
- return
84
- }
85
-
86
- this.running = false
87
- this.globalStats.stop()
88
-
89
- for (const stats of this.peerStats.values()) {
90
- stats.stop()
91
- }
92
-
93
- for (const stats of this.protocolStats.values()) {
94
- stats.stop()
95
- }
96
- }
97
-
98
- /**
99
- * Gets the global `Stats` object
100
- */
101
- getGlobal () {
102
- return this.globalStats
103
- }
104
-
105
- /**
106
- * Returns a list of `PeerId` strings currently being tracked
107
- */
108
- getPeers () {
109
- return Array.from(this.peerStats.keys())
110
- }
111
-
112
- getComponentMetrics () {
113
- return this.systems
114
- }
115
-
116
- updateComponentMetric (update: ComponentMetricsUpdate) {
117
- const { system = 'libp2p', component, metric, value, label, help } = update
118
-
119
- if (!this.systems.has(system)) {
120
- this.systems.set(system, new Map())
121
- }
122
-
123
- const systemMetrics = this.systems.get(system)
124
-
125
- if (systemMetrics == null) {
126
- throw new Error('Unknown metric system')
127
- }
128
-
129
- if (!systemMetrics.has(component)) {
130
- systemMetrics.set(component, new Map())
131
- }
132
-
133
- const componentMetrics = systemMetrics.get(component)
134
-
135
- if (componentMetrics == null) {
136
- throw new Error('Unknown metric component')
137
- }
138
-
139
- componentMetrics.set(metric, {
140
- label,
141
- help,
142
- calculate: typeof value !== 'function' ? () => value : value
143
- })
144
- }
145
-
146
- /**
147
- * Returns the `Stats` object for the given `PeerId` whether it
148
- * is a live peer, or in the disconnected peer LRU cache.
149
- */
150
- forPeer (peerId: PeerId): Stats | undefined {
151
- const idString = peerId.toString()
152
- return this.peerStats.get(idString) ?? this.oldPeers.get(idString)
153
- }
154
-
155
- /**
156
- * Returns a list of all protocol strings currently being tracked
157
- */
158
- getProtocols (): string[] {
159
- return Array.from(this.protocolStats.keys())
160
- }
161
-
162
- /**
163
- * Returns the `Stats` object for the given `protocol`
164
- */
165
- forProtocol (protocol: string): Stats | undefined {
166
- return this.protocolStats.get(protocol)
167
- }
168
-
169
- /**
170
- * Should be called when all connections to a given peer
171
- * have closed. The `Stats` collection for the peer will
172
- * be stopped and moved to an LRU for temporary retention.
173
- */
174
- onPeerDisconnected (peerId: PeerId) {
175
- const idString = peerId.toString()
176
- const peerStats = this.peerStats.get(idString)
177
-
178
- if (peerStats != null) {
179
- peerStats.stop()
180
-
181
- this.peerStats.delete(idString)
182
- this.oldPeers.set(idString, peerStats)
183
- }
184
- }
185
-
186
- /**
187
- * Takes the metadata for a message and tracks it in the
188
- * appropriate categories. If the protocol is present, protocol
189
- * stats will also be tracked.
190
- */
191
- _onMessage (opts: OnMessageOptions) {
192
- if (!this.running) {
193
- return
194
- }
195
-
196
- const { remotePeer, protocol, direction, dataLength } = opts
197
-
198
- const key = directionToEvent[direction]
199
-
200
- let peerStats = this.forPeer(remotePeer)
201
- if (peerStats == null) {
202
- const stats = new DefaultStats(this.statsInit)
203
- this.peerStats.set(remotePeer.toString(), stats)
204
- peerStats = stats
205
- }
206
-
207
- // Peer and global stats
208
- peerStats.push(key, dataLength)
209
- this.globalStats.push(key, dataLength)
210
-
211
- // Protocol specific stats
212
- if (protocol != null) {
213
- let protocolStats = this.forProtocol(protocol)
214
-
215
- if (protocolStats == null) {
216
- const stats = new DefaultStats(this.statsInit)
217
- this.protocolStats.set(protocol, stats)
218
- protocolStats = stats
219
- }
220
-
221
- protocolStats.push(key, dataLength)
222
- }
223
- }
224
-
225
- /**
226
- * Replaces the `PeerId` string with the given `peerId`.
227
- * If stats are already being tracked for the given `peerId`, the
228
- * placeholder stats will be merged with the existing stats.
229
- *
230
- * @param {PeerId} placeholder - A peerId string
231
- * @param {PeerId} peerId
232
- * @returns {void}
233
- */
234
- updatePlaceholder (placeholder: PeerId, peerId: PeerId) {
235
- if (!this.running) {
236
- return
237
- }
238
-
239
- const placeholderString = placeholder.toString()
240
- const placeholderStats = this.peerStats.get(placeholderString) ?? this.oldPeers.get(placeholderString)
241
- const peerIdString = peerId.toString()
242
- const existingStats = this.peerStats.get(peerIdString) ?? this.oldPeers.get(peerIdString)
243
- let mergedStats = placeholderStats
244
-
245
- // If we already have stats, merge the two
246
- if (existingStats != null) {
247
- // If existing, merge
248
- mergedStats = mergeStats(existingStats, mergedStats)
249
- // Attempt to delete from the old peers list just in case it was tracked there
250
- this.oldPeers.remove(peerIdString)
251
- }
252
-
253
- this.peerStats.delete(placeholder.toString())
254
- this.peerStats.set(peerIdString, mergedStats)
255
- mergedStats.start()
256
- }
257
-
258
- /**
259
- * Tracks data running through a given Duplex Iterable `stream`. If
260
- * the `peerId` is not provided, a placeholder string will be created and
261
- * returned. This allows lazy tracking of a peer when the peer is not yet known.
262
- * When the `PeerId` is known, `Metrics.updatePlaceholder` should be called
263
- * with the placeholder string returned from here, and the known `PeerId`.
264
- */
265
- trackStream (opts: TrackStreamOptions): void {
266
- const { stream, remotePeer, protocol } = opts
267
-
268
- if (!this.running) {
269
- return
270
- }
271
-
272
- const source = stream.source
273
- stream.source = each(source, chunk => this._onMessage({
274
- remotePeer,
275
- protocol,
276
- direction: 'in',
277
- dataLength: chunk.byteLength
278
- }))
279
-
280
- const sink = stream.sink
281
- stream.sink = async source => {
282
- return await pipe(
283
- source,
284
- (source) => each(source, chunk => {
285
- this._onMessage({
286
- remotePeer,
287
- protocol,
288
- direction: 'out',
289
- dataLength: chunk.byteLength
290
- })
291
- }),
292
- sink
293
- )
294
- }
295
- }
296
- }
297
-
298
- /**
299
- * Merges `other` into `target`. `target` will be modified
300
- * and returned
301
- */
302
- function mergeStats (target: DefaultStats, other: DefaultStats) {
303
- target.stop()
304
- other.stop()
305
-
306
- // Merge queues
307
- target.queue = [...target.queue, ...other.queue]
308
-
309
- // TODO: how to merge moving averages?
310
- return target
311
- }
@@ -1,53 +0,0 @@
1
- import type { MovingAverage } from '@libp2p/interface-metrics'
2
-
3
- export class DefaultMovingAverage {
4
- public movingAverage: number
5
- public variance: number
6
- public deviation: number
7
- public forecast: number
8
- private readonly timespan: number
9
- private previousTime?: number
10
-
11
- constructor (timespan: number) {
12
- if (typeof timespan !== 'number') {
13
- throw new Error('must provide a timespan to the moving average constructor')
14
- }
15
-
16
- if (timespan <= 0) {
17
- throw new Error('must provide a timespan > 0 to the moving average constructor')
18
- }
19
-
20
- this.timespan = timespan
21
- this.movingAverage = 0
22
- this.variance = 0
23
- this.deviation = 0
24
- this.forecast = 0
25
- }
26
-
27
- alpha (t: number, pt: number) {
28
- return 1 - (Math.exp(-(t - pt) / this.timespan))
29
- }
30
-
31
- push (time: number, value: number) {
32
- if (this.previousTime != null) {
33
- // calculate moving average
34
- const a = this.alpha(time, this.previousTime)
35
- const diff = value - this.movingAverage
36
- const incr = a * diff
37
- this.movingAverage = a * value + (1 - a) * this.movingAverage
38
- // calculate variance & deviation
39
- this.variance = (1 - a) * (this.variance + diff * incr)
40
- this.deviation = Math.sqrt(this.variance)
41
- // calculate forecast
42
- this.forecast = this.movingAverage + a * diff
43
- } else {
44
- this.movingAverage = value
45
- }
46
-
47
- this.previousTime = time
48
- }
49
- }
50
-
51
- export function createMovingAverage (timespan: number): MovingAverage {
52
- return new DefaultMovingAverage(timespan)
53
- }
@@ -1,238 +0,0 @@
1
- import { CustomEvent, EventEmitter } from '@libp2p/interfaces/events'
2
- import { createMovingAverage } from './moving-average.js'
3
- // @ts-expect-error no types
4
- import retimer from 'retimer'
5
- import type { MovingAverages, Stats, TransferStats } from '@libp2p/interface-metrics'
6
-
7
- export interface StatsEvents {
8
- 'update': CustomEvent<TransferStats>
9
- }
10
-
11
- export interface StatsInit {
12
- enabled: boolean
13
- initialCounters: ['dataReceived', 'dataSent']
14
- movingAverageIntervals: number[]
15
- computeThrottleMaxQueueSize: number
16
- computeThrottleTimeout: number
17
- }
18
-
19
- export class DefaultStats extends EventEmitter<StatsEvents> implements Stats {
20
- private readonly enabled: boolean
21
- public queue: Array<[string, number, number]>
22
- private stats: TransferStats
23
- private frequencyLastTime: number
24
- private frequencyAccumulators: Record<string, number>
25
- private movingAverages: MovingAverages
26
- private timeout?: any
27
- private readonly computeThrottleMaxQueueSize: number
28
- private readonly computeThrottleTimeout: number
29
- private readonly movingAverageIntervals: number[]
30
-
31
- /**
32
- * A queue based manager for stat processing
33
- */
34
- constructor (init: StatsInit) {
35
- super()
36
-
37
- this.enabled = init.enabled
38
- this.queue = []
39
- this.stats = {
40
- dataReceived: 0n,
41
- dataSent: 0n
42
- }
43
- this.frequencyLastTime = Date.now()
44
- this.frequencyAccumulators = {}
45
- this.movingAverages = {
46
- dataReceived: [],
47
- dataSent: []
48
- }
49
- this.computeThrottleMaxQueueSize = init.computeThrottleMaxQueueSize
50
- this.computeThrottleTimeout = init.computeThrottleTimeout
51
-
52
- this._update = this._update.bind(this)
53
-
54
- this.movingAverageIntervals = init.movingAverageIntervals
55
-
56
- for (let i = 0; i < init.initialCounters.length; i++) {
57
- const key = init.initialCounters[i]
58
- this.stats[key] = 0n
59
- this.movingAverages[key] = []
60
-
61
- for (let k = 0; k < this.movingAverageIntervals.length; k++) {
62
- const interval = this.movingAverageIntervals[k]
63
- const ma = this.movingAverages[key][interval] = createMovingAverage(interval)
64
- ma.push(this.frequencyLastTime, 0)
65
- }
66
- }
67
- }
68
-
69
- /**
70
- * Initializes the internal timer if there are items in the queue. This
71
- * should only need to be called if `Stats.stop` was previously called, as
72
- * `Stats.push` will also start the processing
73
- */
74
- start () {
75
- if (!this.enabled) {
76
- return
77
- }
78
-
79
- if (this.queue.length > 0) {
80
- this._resetComputeTimeout()
81
- }
82
- }
83
-
84
- /**
85
- * Stops processing and computing of stats by clearing the internal
86
- * timer
87
- */
88
- stop () {
89
- if (this.timeout != null) {
90
- this.timeout.clear()
91
- this.timeout = null
92
- }
93
- }
94
-
95
- /**
96
- * Returns a clone of the current stats.
97
- */
98
- getSnapshot () {
99
- return Object.assign({}, this.stats)
100
- }
101
-
102
- /**
103
- * Returns a clone of the internal movingAverages
104
- */
105
- getMovingAverages (): MovingAverages {
106
- return Object.assign({}, this.movingAverages)
107
- }
108
-
109
- /**
110
- * Pushes the given operation data to the queue, along with the
111
- * current Timestamp, then resets the update timer.
112
- */
113
- push (counter: string, inc: number) {
114
- this.queue.push([counter, inc, Date.now()])
115
- this._resetComputeTimeout()
116
- }
117
-
118
- /**
119
- * Resets the timeout for triggering updates.
120
- */
121
- _resetComputeTimeout () {
122
- this.timeout = retimer(this._update, this._nextTimeout())
123
- }
124
-
125
- /**
126
- * Calculates and returns the timeout for the next update based on
127
- * the urgency of the update.
128
- */
129
- _nextTimeout () {
130
- // calculate the need for an update, depending on the queue length
131
- const urgency = this.queue.length / this.computeThrottleMaxQueueSize
132
- const timeout = Math.max(this.computeThrottleTimeout * (1 - urgency), 0)
133
- return timeout
134
- }
135
-
136
- /**
137
- * If there are items in the queue, they will will be processed and
138
- * the frequency for all items will be updated based on the Timestamp
139
- * of the last item in the queue. The `update` event will also be emitted
140
- * with the latest stats.
141
- *
142
- * If there are no items in the queue, no action is taken.
143
- */
144
- _update () {
145
- this.timeout = null
146
- if (this.queue.length > 0) {
147
- let last: [string, number, number] = ['', 0, 0]
148
-
149
- for (last of this.queue) {
150
- this._applyOp(last)
151
- }
152
-
153
- this.queue = []
154
-
155
- if (last.length > 2 && last[0] !== '') {
156
- this._updateFrequency(last[2]) // contains timestamp of last op
157
- }
158
-
159
- this.dispatchEvent(new CustomEvent<TransferStats>('update', {
160
- detail: this.stats
161
- }))
162
- }
163
- }
164
-
165
- /**
166
- * For each key in the stats, the frequency and moving averages
167
- * will be updated via Stats._updateFrequencyFor based on the time
168
- * difference between calls to this method.
169
- */
170
- _updateFrequency (latestTime: number) {
171
- const timeDiff = latestTime - this.frequencyLastTime
172
-
173
- this._updateFrequencyFor('dataReceived', timeDiff, latestTime)
174
- this._updateFrequencyFor('dataSent', timeDiff, latestTime)
175
-
176
- this.frequencyLastTime = latestTime
177
- }
178
-
179
- /**
180
- * Updates the `movingAverages` for the given `key` and also
181
- * resets the `frequencyAccumulator` for the `key`.
182
- */
183
- _updateFrequencyFor (key: 'dataReceived' | 'dataSent', timeDiffMS: number, latestTime: number) {
184
- const count = this.frequencyAccumulators[key] ?? 0
185
- this.frequencyAccumulators[key] = 0
186
- // if `timeDiff` is zero, `hz` becomes Infinity, so we fallback to 1ms
187
- const safeTimeDiff = timeDiffMS ?? 1
188
- const hz = (count / safeTimeDiff) * 1000
189
-
190
- let movingAverages = this.movingAverages[key]
191
- if (movingAverages == null) {
192
- movingAverages = this.movingAverages[key] = []
193
- }
194
-
195
- const intervals = this.movingAverageIntervals
196
-
197
- for (let i = 0; i < intervals.length; i++) {
198
- const movingAverageInterval = intervals[i]
199
- let movingAverage = movingAverages[movingAverageInterval]
200
- if (movingAverage == null) {
201
- movingAverage = movingAverages[movingAverageInterval] = createMovingAverage(movingAverageInterval)
202
- }
203
- movingAverage.push(latestTime, hz)
204
- }
205
- }
206
-
207
- /**
208
- * For the given operation, `op`, the stats and `frequencyAccumulator`
209
- * will be updated or initialized if they don't already exist.
210
- */
211
- _applyOp (op: [string, number, number]) {
212
- const key = op[0]
213
- const inc = op[1]
214
-
215
- if (typeof inc !== 'number') {
216
- throw new Error('invalid increment number')
217
- }
218
-
219
- let n: bigint
220
-
221
- if (!Object.prototype.hasOwnProperty.call(this.stats, key)) {
222
- // @ts-expect-error cannot index type with key
223
- n = this.stats[key] = 0n
224
- } else {
225
- // @ts-expect-error cannot index type with key
226
- n = this.stats[key]
227
- }
228
-
229
- // @ts-expect-error cannot index type with key
230
- this.stats[key] = n + BigInt(inc)
231
-
232
- if (this.frequencyAccumulators[key] == null) {
233
- this.frequencyAccumulators[key] = 0
234
- }
235
-
236
- this.frequencyAccumulators[key] += inc
237
- }
238
- }