@libp2p/perf 1.1.15-dfbe0cc0 → 2.0.0-fb8a6f188
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.
- package/README.md +44 -33
- package/dist/index.min.js +1 -1
- package/dist/src/constants.d.ts +4 -1
- package/dist/src/constants.d.ts.map +1 -1
- package/dist/src/constants.js +4 -1
- package/dist/src/constants.js.map +1 -1
- package/dist/src/index.d.ts +63 -34
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +46 -138
- package/dist/src/index.js.map +1 -1
- package/dist/src/perf-service.d.ts +21 -0
- package/dist/src/perf-service.d.ts.map +1 -0
- package/dist/src/perf-service.js +169 -0
- package/dist/src/perf-service.js.map +1 -0
- package/package.json +11 -16
- package/src/constants.ts +4 -1
- package/src/index.ts +68 -160
- package/src/perf-service.ts +207 -0
- package/dist/src/main.d.ts +0 -2
- package/dist/src/main.d.ts.map +0 -1
- package/dist/src/main.js +0 -107
- package/dist/src/main.js.map +0 -1
- package/src/main.ts +0 -118
@@ -0,0 +1,169 @@
|
|
1
|
+
import { logger } from '@libp2p/logger';
|
2
|
+
import { pushable } from 'it-pushable';
|
3
|
+
import { MAX_INBOUND_STREAMS, MAX_OUTBOUND_STREAMS, PROTOCOL_NAME, RUN_ON_TRANSIENT_CONNECTION, WRITE_BLOCK_SIZE } from './constants.js';
|
4
|
+
const log = logger('libp2p:perf');
|
5
|
+
export class PerfService {
|
6
|
+
protocol;
|
7
|
+
components;
|
8
|
+
started;
|
9
|
+
databuf;
|
10
|
+
writeBlockSize;
|
11
|
+
maxInboundStreams;
|
12
|
+
maxOutboundStreams;
|
13
|
+
runOnTransientConnection;
|
14
|
+
constructor(components, init = {}) {
|
15
|
+
this.components = components;
|
16
|
+
this.started = false;
|
17
|
+
this.protocol = init.protocolName ?? PROTOCOL_NAME;
|
18
|
+
this.writeBlockSize = init.writeBlockSize ?? WRITE_BLOCK_SIZE;
|
19
|
+
this.databuf = new ArrayBuffer(this.writeBlockSize);
|
20
|
+
this.maxInboundStreams = init.maxInboundStreams ?? MAX_INBOUND_STREAMS;
|
21
|
+
this.maxOutboundStreams = init.maxOutboundStreams ?? MAX_OUTBOUND_STREAMS;
|
22
|
+
this.runOnTransientConnection = init.runOnTransientConnection ?? RUN_ON_TRANSIENT_CONNECTION;
|
23
|
+
}
|
24
|
+
async start() {
|
25
|
+
await this.components.registrar.handle(this.protocol, (data) => {
|
26
|
+
void this.handleMessage(data).catch((err) => {
|
27
|
+
log.error('error handling perf protocol message', err);
|
28
|
+
});
|
29
|
+
}, {
|
30
|
+
maxInboundStreams: this.maxInboundStreams,
|
31
|
+
maxOutboundStreams: this.maxOutboundStreams,
|
32
|
+
runOnTransientConnection: this.runOnTransientConnection
|
33
|
+
});
|
34
|
+
this.started = true;
|
35
|
+
}
|
36
|
+
async stop() {
|
37
|
+
await this.components.registrar.unhandle(this.protocol);
|
38
|
+
this.started = false;
|
39
|
+
}
|
40
|
+
isStarted() {
|
41
|
+
return this.started;
|
42
|
+
}
|
43
|
+
async handleMessage(data) {
|
44
|
+
const { stream } = data;
|
45
|
+
try {
|
46
|
+
const writeBlockSize = this.writeBlockSize;
|
47
|
+
let bytesToSendBack;
|
48
|
+
for await (const buf of stream.source) {
|
49
|
+
if (bytesToSendBack == null) {
|
50
|
+
// downcast 64 to 52 bits to avoid bigint arithmetic performance penalty
|
51
|
+
bytesToSendBack = Number(buf.getBigUint64(0, false));
|
52
|
+
}
|
53
|
+
// Ingest all the bufs and wait for the read side to close
|
54
|
+
}
|
55
|
+
if (bytesToSendBack == null) {
|
56
|
+
throw new Error('bytesToSendBack was not set');
|
57
|
+
}
|
58
|
+
const uint8Buf = new Uint8Array(this.databuf);
|
59
|
+
await stream.sink(async function* () {
|
60
|
+
while (bytesToSendBack > 0) {
|
61
|
+
let toSend = writeBlockSize;
|
62
|
+
if (toSend > bytesToSendBack) {
|
63
|
+
toSend = bytesToSendBack;
|
64
|
+
}
|
65
|
+
bytesToSendBack = bytesToSendBack - toSend;
|
66
|
+
yield uint8Buf.subarray(0, toSend);
|
67
|
+
}
|
68
|
+
}());
|
69
|
+
}
|
70
|
+
catch (err) {
|
71
|
+
stream.abort(err);
|
72
|
+
}
|
73
|
+
}
|
74
|
+
async *measurePerformance(ma, sendBytes, receiveBytes, options = {}) {
|
75
|
+
log('opening stream on protocol %s to %a', this.protocol, ma);
|
76
|
+
const uint8Buf = new Uint8Array(this.databuf);
|
77
|
+
const writeBlockSize = this.writeBlockSize;
|
78
|
+
// start time should include connection establishment
|
79
|
+
const initialStartTime = Date.now();
|
80
|
+
const connection = await this.components.connectionManager.openConnection(ma, {
|
81
|
+
...options,
|
82
|
+
force: options.reuseExistingConnection !== true
|
83
|
+
});
|
84
|
+
const stream = await connection.newStream(this.protocol, options);
|
85
|
+
let lastAmountOfBytesSent = 0;
|
86
|
+
let lastReportedTime = Date.now();
|
87
|
+
let totalBytesSent = 0;
|
88
|
+
// tell the remote how many bytes we will send. Up cast to 64 bit number
|
89
|
+
// as if we send as ui32 we limit total transfer size to 4GB
|
90
|
+
const view = new DataView(this.databuf);
|
91
|
+
view.setBigUint64(0, BigInt(receiveBytes), false);
|
92
|
+
log('sending %i bytes to %p', sendBytes, connection.remotePeer);
|
93
|
+
try {
|
94
|
+
const sendOutput = pushable({
|
95
|
+
objectMode: true
|
96
|
+
});
|
97
|
+
void stream.sink(async function* () {
|
98
|
+
// Send the number of bytes to receive
|
99
|
+
yield uint8Buf.subarray(0, 8);
|
100
|
+
while (sendBytes > 0) {
|
101
|
+
options.signal?.throwIfAborted();
|
102
|
+
let toSend = writeBlockSize;
|
103
|
+
if (toSend > sendBytes) {
|
104
|
+
toSend = sendBytes;
|
105
|
+
}
|
106
|
+
sendBytes = sendBytes - toSend;
|
107
|
+
yield uint8Buf.subarray(0, toSend);
|
108
|
+
if (Date.now() - lastReportedTime > 1000) {
|
109
|
+
sendOutput.push({
|
110
|
+
type: 'intermediary',
|
111
|
+
timeSeconds: (Date.now() - lastReportedTime) / 1000,
|
112
|
+
uploadBytes: lastAmountOfBytesSent,
|
113
|
+
downloadBytes: 0
|
114
|
+
});
|
115
|
+
// record last reported time after `console.log` because it can
|
116
|
+
// affect benchmark timings
|
117
|
+
lastReportedTime = Date.now();
|
118
|
+
lastAmountOfBytesSent = 0;
|
119
|
+
}
|
120
|
+
lastAmountOfBytesSent += toSend;
|
121
|
+
totalBytesSent += toSend;
|
122
|
+
}
|
123
|
+
sendOutput.end();
|
124
|
+
}())
|
125
|
+
.catch(err => {
|
126
|
+
sendOutput.end(err);
|
127
|
+
});
|
128
|
+
yield* sendOutput;
|
129
|
+
// Read the received bytes
|
130
|
+
let lastAmountOfBytesReceived = 0;
|
131
|
+
lastReportedTime = Date.now();
|
132
|
+
let totalBytesReceived = 0;
|
133
|
+
for await (const buf of stream.source) {
|
134
|
+
options.signal?.throwIfAborted();
|
135
|
+
if (Date.now() - lastReportedTime > 1000) {
|
136
|
+
yield {
|
137
|
+
type: 'intermediary',
|
138
|
+
timeSeconds: (Date.now() - lastReportedTime) / 1000,
|
139
|
+
uploadBytes: 0,
|
140
|
+
downloadBytes: lastAmountOfBytesReceived
|
141
|
+
};
|
142
|
+
// record last reported time after `console.log` because it can
|
143
|
+
// affect benchmark timings
|
144
|
+
lastReportedTime = Date.now();
|
145
|
+
lastAmountOfBytesReceived = 0;
|
146
|
+
}
|
147
|
+
lastAmountOfBytesReceived += buf.byteLength;
|
148
|
+
totalBytesReceived += buf.byteLength;
|
149
|
+
}
|
150
|
+
if (totalBytesReceived !== receiveBytes) {
|
151
|
+
throw new Error(`Expected to receive ${receiveBytes} bytes, but received ${totalBytesReceived}`);
|
152
|
+
}
|
153
|
+
yield {
|
154
|
+
type: 'final',
|
155
|
+
timeSeconds: (Date.now() - initialStartTime) / 1000,
|
156
|
+
uploadBytes: totalBytesSent,
|
157
|
+
downloadBytes: totalBytesReceived
|
158
|
+
};
|
159
|
+
log('performed %s to %p', this.protocol, connection.remotePeer);
|
160
|
+
await stream.close();
|
161
|
+
}
|
162
|
+
catch (err) {
|
163
|
+
log('error sending %s bytes to %p: %s', totalBytesSent, connection.remotePeer, err);
|
164
|
+
stream.abort(err);
|
165
|
+
throw err;
|
166
|
+
}
|
167
|
+
}
|
168
|
+
}
|
169
|
+
//# sourceMappingURL=perf-service.js.map
|
@@ -0,0 +1 @@
|
|
1
|
+
{"version":3,"file":"perf-service.js","sourceRoot":"","sources":["../../src/perf-service.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@libp2p/perf",
|
3
|
-
"version": "
|
3
|
+
"version": "2.0.0-fb8a6f188",
|
4
4
|
"description": "Implementation of Perf Protocol",
|
5
5
|
"author": "@maschad / @marcopolo",
|
6
6
|
"license": "Apache-2.0 OR MIT",
|
@@ -48,22 +48,17 @@
|
|
48
48
|
"renderResults": "node dist/src/renderResults.js"
|
49
49
|
},
|
50
50
|
"dependencies": {
|
51
|
-
"@
|
52
|
-
"@
|
53
|
-
"@libp2p/
|
54
|
-
"@
|
55
|
-
"
|
56
|
-
"@libp2p/interface-internal": "0.1.8-dfbe0cc0",
|
57
|
-
"@libp2p/interfaces": "3.3.2",
|
58
|
-
"@libp2p/logger": "3.0.5-dfbe0cc0",
|
59
|
-
"@libp2p/peer-id-factory": "3.0.7-dfbe0cc0",
|
60
|
-
"@libp2p/tcp": "8.0.11-dfbe0cc0",
|
61
|
-
"@multiformats/multiaddr": "^12.1.5",
|
62
|
-
"libp2p": "0.46.18-dfbe0cc0",
|
63
|
-
"uint8arrays": "^4.0.6",
|
64
|
-
"yargs": "^17.7.2"
|
51
|
+
"@libp2p/interface": "0.1.5-fb8a6f188",
|
52
|
+
"@libp2p/interface-internal": "0.1.8-fb8a6f188",
|
53
|
+
"@libp2p/logger": "3.0.5-fb8a6f188",
|
54
|
+
"@multiformats/multiaddr": "^12.1.10",
|
55
|
+
"it-pushable": "^3.2.1"
|
65
56
|
},
|
66
57
|
"devDependencies": {
|
67
|
-
"
|
58
|
+
"@libp2p/interface-compliance-tests": "4.1.4-fb8a6f188",
|
59
|
+
"aegir": "^41.0.2",
|
60
|
+
"it-last": "^3.0.3",
|
61
|
+
"it-pair": "^2.0.6",
|
62
|
+
"sinon-ts": "^2.0.0"
|
68
63
|
}
|
69
64
|
}
|
package/src/constants.ts
CHANGED
package/src/index.ts
CHANGED
@@ -1,71 +1,95 @@
|
|
1
1
|
/**
|
2
2
|
* @packageDocumentation
|
3
3
|
*
|
4
|
-
* The
|
5
|
-
* addresses.
|
4
|
+
* The {@link PerfService} implements the [perf protocol](https://github.com/libp2p/specs/blob/master/perf/perf.md), which can be used to measure transfer performance within and across libp2p implementations.
|
6
5
|
*
|
7
6
|
* @example
|
8
7
|
*
|
9
8
|
* ```typescript
|
10
|
-
* import {
|
11
|
-
* import {
|
9
|
+
* import { noise } from '@chainsafe/libp2p-noise'
|
10
|
+
* import { yamux } from '@chainsafe/libp2p-yamux'
|
11
|
+
* import { mplex } from '@libp2p/mplex'
|
12
|
+
* import { tcp } from '@libp2p/tcp'
|
13
|
+
* import { createLibp2p, type Libp2p } from 'libp2p'
|
14
|
+
* import { plaintext } from 'libp2p/insecure'
|
15
|
+
* import { perfService, type PerfService } from '@libp2p/perf'
|
12
16
|
*
|
13
|
-
* const
|
14
|
-
*
|
15
|
-
*
|
16
|
-
* ]
|
17
|
-
* })
|
18
|
-
* ```
|
19
|
-
*
|
20
|
-
* The `measurePerformance` function can be used to measure the latency and throughput of a connection.
|
21
|
-
* server. This will not work in browsers.
|
17
|
+
* const ONE_MEG = 1024 * 1024
|
18
|
+
* const UPLOAD_BYTES = ONE_MEG * 1024
|
19
|
+
* const DOWNLOAD_BYTES = ONE_MEG * 1024
|
22
20
|
*
|
23
|
-
*
|
24
|
-
*
|
25
|
-
*
|
26
|
-
*
|
27
|
-
*
|
21
|
+
* async function createNode (): Promise<Libp2p<{ perf: PerfService }>> {
|
22
|
+
* return createLibp2p({
|
23
|
+
* addresses: {
|
24
|
+
* listen: [
|
25
|
+
* '/ip4/0.0.0.0/tcp/0'
|
26
|
+
* ]
|
27
|
+
* },
|
28
|
+
* transports: [
|
29
|
+
* tcp()
|
30
|
+
* ],
|
31
|
+
* connectionEncryption: [
|
32
|
+
* noise(), plaintext()
|
33
|
+
* ],
|
34
|
+
* streamMuxers: [
|
35
|
+
* yamux(), mplex()
|
36
|
+
* ],
|
37
|
+
* services: {
|
38
|
+
* perf: perfService()
|
39
|
+
* }
|
40
|
+
* })
|
41
|
+
* }
|
28
42
|
*
|
29
|
-
* const
|
30
|
-
*
|
31
|
-
* perf: perfService()
|
32
|
-
* ]
|
33
|
-
* })
|
43
|
+
* const libp2p1 = await createNode()
|
44
|
+
* const libp2p2 = await createNode()
|
34
45
|
*
|
35
|
-
* const
|
36
|
-
*
|
37
|
-
*
|
38
|
-
*
|
39
|
-
* await node.services.perf.measurePerformance(startTime, connection, BigInt(uploadBytes), BigInt(downloadBytes))
|
46
|
+
* for await (const output of libp2p1.services.perf.measurePerformance(libp2p2.getMultiaddrs()[0], UPLOAD_BYTES, DOWNLOAD_BYTES)) {
|
47
|
+
* console.info(output)
|
48
|
+
* }
|
40
49
|
*
|
50
|
+
* await libp2p1.stop()
|
51
|
+
* await libp2p2.stop()
|
41
52
|
* ```
|
42
53
|
*/
|
43
54
|
|
44
|
-
import {
|
45
|
-
import {
|
46
|
-
import type { Connection } from '@libp2p/interface/connection'
|
47
|
-
import type { Startable } from '@libp2p/interface/startable'
|
55
|
+
import { PerfService as PerfServiceClass } from './perf-service.js'
|
56
|
+
import type { AbortOptions } from '@libp2p/interface'
|
48
57
|
import type { ConnectionManager } from '@libp2p/interface-internal/connection-manager'
|
49
|
-
import type {
|
50
|
-
import type {
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
58
|
+
import type { Registrar } from '@libp2p/interface-internal/registrar'
|
59
|
+
import type { Multiaddr } from '@multiformats/multiaddr'
|
60
|
+
|
61
|
+
export interface PerfOptions extends AbortOptions {
|
62
|
+
/**
|
63
|
+
* By default measuring perf should include the time it takes to establish a
|
64
|
+
* connection, so a new connection will be opened for every performance run.
|
65
|
+
*
|
66
|
+
* To override this and re-use an existing connection if one is present, pass
|
67
|
+
* `true` here. (default: false)
|
68
|
+
*/
|
69
|
+
reuseExistingConnection?: boolean
|
57
70
|
}
|
58
71
|
|
59
72
|
export interface PerfService {
|
60
|
-
measurePerformance(
|
73
|
+
measurePerformance(multiaddr: Multiaddr, sendBytes: number, recvBytes: number, options?: PerfOptions): AsyncGenerator<PerfOutput>
|
74
|
+
}
|
75
|
+
|
76
|
+
export interface PerfOutput {
|
77
|
+
type: 'intermediary' | 'final'
|
78
|
+
timeSeconds: number
|
79
|
+
uploadBytes: number
|
80
|
+
downloadBytes: number
|
61
81
|
}
|
62
82
|
|
63
83
|
export interface PerfServiceInit {
|
64
84
|
protocolName?: string
|
65
85
|
maxInboundStreams?: number
|
66
86
|
maxOutboundStreams?: number
|
67
|
-
|
68
|
-
|
87
|
+
runOnTransientConnection?: boolean
|
88
|
+
|
89
|
+
/**
|
90
|
+
* Data sent/received will be sent in chunks of this size (default: 64KiB)
|
91
|
+
*/
|
92
|
+
writeBlockSize?: number
|
69
93
|
}
|
70
94
|
|
71
95
|
export interface PerfServiceComponents {
|
@@ -73,122 +97,6 @@ export interface PerfServiceComponents {
|
|
73
97
|
connectionManager: ConnectionManager
|
74
98
|
}
|
75
99
|
|
76
|
-
class DefaultPerfService implements Startable, PerfService {
|
77
|
-
public readonly protocol: string
|
78
|
-
private readonly components: PerfServiceComponents
|
79
|
-
private started: boolean
|
80
|
-
private readonly databuf: ArrayBuffer
|
81
|
-
private readonly writeBlockSize: bigint
|
82
|
-
|
83
|
-
constructor (components: PerfServiceComponents, init: PerfServiceInit) {
|
84
|
-
this.components = components
|
85
|
-
this.started = false
|
86
|
-
this.protocol = init.protocolName ?? PROTOCOL_NAME
|
87
|
-
this.writeBlockSize = init.writeBlockSize ?? WRITE_BLOCK_SIZE
|
88
|
-
this.databuf = new ArrayBuffer(Number(init.writeBlockSize))
|
89
|
-
}
|
90
|
-
|
91
|
-
async start (): Promise<void> {
|
92
|
-
await this.components.registrar.handle(this.protocol, (data: IncomingStreamData) => {
|
93
|
-
void this.handleMessage(data).catch((err) => {
|
94
|
-
log.error('error handling perf protocol message', err)
|
95
|
-
})
|
96
|
-
})
|
97
|
-
this.started = true
|
98
|
-
}
|
99
|
-
|
100
|
-
async stop (): Promise<void> {
|
101
|
-
await this.components.registrar.unhandle(this.protocol)
|
102
|
-
this.started = false
|
103
|
-
}
|
104
|
-
|
105
|
-
isStarted (): boolean {
|
106
|
-
return this.started
|
107
|
-
}
|
108
|
-
|
109
|
-
async handleMessage (data: IncomingStreamData): Promise<void> {
|
110
|
-
const { stream } = data
|
111
|
-
|
112
|
-
const writeBlockSize = this.writeBlockSize
|
113
|
-
|
114
|
-
let bytesToSendBack: bigint | null = null
|
115
|
-
|
116
|
-
for await (const buf of stream.source) {
|
117
|
-
if (bytesToSendBack === null) {
|
118
|
-
bytesToSendBack = BigInt(buf.getBigUint64(0, false))
|
119
|
-
}
|
120
|
-
// Ingest all the bufs and wait for the read side to close
|
121
|
-
}
|
122
|
-
|
123
|
-
const uint8Buf = new Uint8Array(this.databuf)
|
124
|
-
|
125
|
-
if (bytesToSendBack === null) {
|
126
|
-
throw new Error('bytesToSendBack was not set')
|
127
|
-
}
|
128
|
-
|
129
|
-
await stream.sink(async function * () {
|
130
|
-
while (bytesToSendBack > 0n) {
|
131
|
-
let toSend: bigint = writeBlockSize
|
132
|
-
if (toSend > bytesToSendBack) {
|
133
|
-
toSend = bytesToSendBack
|
134
|
-
}
|
135
|
-
bytesToSendBack = bytesToSendBack - toSend
|
136
|
-
yield uint8Buf.subarray(0, Number(toSend))
|
137
|
-
}
|
138
|
-
}())
|
139
|
-
}
|
140
|
-
|
141
|
-
async measurePerformance (startTime: number, connection: Connection, sendBytes: bigint, recvBytes: bigint, options: AbortOptions = {}): Promise<number> {
|
142
|
-
log('opening stream on protocol %s to %p', this.protocol, connection.remotePeer)
|
143
|
-
|
144
|
-
const uint8Buf = new Uint8Array(this.databuf)
|
145
|
-
|
146
|
-
const writeBlockSize = this.writeBlockSize
|
147
|
-
|
148
|
-
const stream = await connection.newStream([this.protocol], options)
|
149
|
-
|
150
|
-
// Convert sendBytes to uint64 big endian buffer
|
151
|
-
const view = new DataView(this.databuf)
|
152
|
-
view.setBigInt64(0, recvBytes, false)
|
153
|
-
|
154
|
-
log('sending %i bytes to %p', sendBytes, connection.remotePeer)
|
155
|
-
try {
|
156
|
-
await stream.sink((async function * () {
|
157
|
-
// Send the number of bytes to receive
|
158
|
-
yield uint8Buf.subarray(0, 8)
|
159
|
-
// Send the number of bytes to send
|
160
|
-
while (sendBytes > 0n) {
|
161
|
-
let toSend: bigint = writeBlockSize
|
162
|
-
if (toSend > sendBytes) {
|
163
|
-
toSend = sendBytes
|
164
|
-
}
|
165
|
-
sendBytes = sendBytes - toSend
|
166
|
-
yield uint8Buf.subarray(0, Number(toSend))
|
167
|
-
}
|
168
|
-
})())
|
169
|
-
|
170
|
-
// Read the received bytes
|
171
|
-
let actualRecvdBytes = BigInt(0)
|
172
|
-
for await (const buf of stream.source) {
|
173
|
-
actualRecvdBytes += BigInt(buf.length)
|
174
|
-
}
|
175
|
-
|
176
|
-
if (actualRecvdBytes !== recvBytes) {
|
177
|
-
throw new Error(`Expected to receive ${recvBytes} bytes, but received ${actualRecvdBytes}`)
|
178
|
-
}
|
179
|
-
} catch (err) {
|
180
|
-
log('error sending %i bytes to %p: %s', sendBytes, connection.remotePeer, err)
|
181
|
-
throw err
|
182
|
-
} finally {
|
183
|
-
log('performed %s to %p', this.protocol, connection.remotePeer)
|
184
|
-
await stream.close()
|
185
|
-
}
|
186
|
-
|
187
|
-
// Return the latency
|
188
|
-
return Date.now() - startTime
|
189
|
-
}
|
190
|
-
}
|
191
|
-
|
192
100
|
export function perfService (init: PerfServiceInit = {}): (components: PerfServiceComponents) => PerfService {
|
193
|
-
return (components) => new
|
101
|
+
return (components) => new PerfServiceClass(components, init)
|
194
102
|
}
|