aedes 0.51.3 → 1.0.1

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 (68) hide show
  1. package/.claude/settings.local.json +12 -0
  2. package/.github/actions/sticky-pr-comment/action.yml +55 -0
  3. package/.github/workflows/benchmark-compare-serial.yml +60 -0
  4. package/.github/workflows/ci.yml +12 -17
  5. package/.release-it.json +18 -0
  6. package/.taprc +15 -6
  7. package/README.md +6 -4
  8. package/aedes.d.ts +0 -6
  9. package/aedes.js +270 -242
  10. package/benchmarks/README.md +33 -0
  11. package/benchmarks/pingpong.js +94 -25
  12. package/benchmarks/receiver.js +77 -0
  13. package/benchmarks/report.js +150 -0
  14. package/benchmarks/runBenchmarks.js +118 -0
  15. package/benchmarks/sender.js +86 -0
  16. package/benchmarks/server.js +19 -18
  17. package/checkVersion.js +20 -0
  18. package/docs/Aedes.md +66 -8
  19. package/docs/Client.md +3 -4
  20. package/docs/Examples.md +39 -22
  21. package/docs/MIGRATION.md +50 -0
  22. package/eslint.config.js +8 -0
  23. package/example.js +51 -40
  24. package/examples/clusters/index.js +28 -23
  25. package/examples/clusters/package.json +10 -6
  26. package/lib/client.js +405 -306
  27. package/lib/handlers/connect.js +42 -38
  28. package/lib/handlers/index.js +9 -11
  29. package/lib/handlers/ping.js +2 -3
  30. package/lib/handlers/puback.js +5 -5
  31. package/lib/handlers/publish.js +29 -14
  32. package/lib/handlers/pubrec.js +9 -17
  33. package/lib/handlers/pubrel.js +34 -25
  34. package/lib/handlers/subscribe.js +47 -43
  35. package/lib/handlers/unsubscribe.js +16 -19
  36. package/lib/qos-packet.js +14 -17
  37. package/lib/utils.js +5 -12
  38. package/lib/write.js +4 -5
  39. package/package.json +139 -136
  40. package/test/auth.js +468 -804
  41. package/test/basic.js +613 -575
  42. package/test/bridge.js +44 -40
  43. package/test/client-pub-sub.js +531 -504
  44. package/test/close_socket_by_other_party.js +137 -102
  45. package/test/connect.js +487 -484
  46. package/test/drain-timeout.js +593 -0
  47. package/test/drain-toxiproxy.js +620 -0
  48. package/test/events.js +173 -145
  49. package/test/helper.js +351 -73
  50. package/test/keep-alive.js +40 -67
  51. package/test/meta.js +257 -210
  52. package/test/not-blocking.js +93 -197
  53. package/test/qos1.js +464 -554
  54. package/test/qos2.js +308 -393
  55. package/test/regr-21.js +39 -21
  56. package/test/require.cjs +22 -0
  57. package/test/retain.js +349 -398
  58. package/test/topics.js +176 -183
  59. package/test/types/aedes.test-d.ts +4 -8
  60. package/test/will.js +310 -428
  61. package/types/instance.d.ts +40 -35
  62. package/types/packet.d.ts +10 -10
  63. package/.coveralls.yml +0 -1
  64. package/benchmarks/bombing.js +0 -34
  65. package/benchmarks/bombingQoS1.js +0 -36
  66. package/benchmarks/throughputCounter.js +0 -23
  67. package/benchmarks/throughputCounterQoS1.js +0 -33
  68. package/types/.eslintrc.json +0 -47
@@ -1,36 +1,115 @@
1
1
  #! /usr/bin/env node
2
-
3
- const mqtt = require('mqtt')
4
- const convertHrtime = require('convert-hrtime')
5
- const mode = require('compute-mode')
6
- const client = mqtt.connect({ port: 1883, host: 'localhost', clean: true, keepalive: 0 })
2
+ import { parseArgs } from 'node:util'
3
+ import { hrtime } from 'node:process'
4
+ import mqtt from 'mqtt'
7
5
  const interval = 5000
8
6
 
9
- let sent = 0
10
- const latencies = []
7
+ function processsLatencies (latencies, counter) {
8
+ let total = 0
9
+ let count = 0
10
+ let median
11
+ let perc95
12
+ let perc99
13
+ const posMedian = Math.floor(counter / 2)
14
+ const pos95 = Math.floor(counter * 0.95)
15
+ const pos99 = Math.floor(counter * 0.99)
16
+ // sort keys from smallest to largest
17
+ const keys = Object.keys(latencies).sort((a, b) => a - b)
18
+ for (const key of keys) {
19
+ const value = latencies[key]
20
+ total += value * key
21
+ count += value
22
+
23
+ if (count >= posMedian && median === undefined) {
24
+ median = key
25
+ }
26
+ if (count >= pos95 && perc95 === undefined) {
27
+ perc95 = key
28
+ }
29
+ if (count >= pos99 && perc99 === undefined) {
30
+ perc99 = key
31
+ }
32
+ }
33
+ return {
34
+ buckets: keys.length,
35
+ mean: Math.floor(total / counter),
36
+ minimum: keys[0],
37
+ maximum: keys.pop(),
38
+ median,
39
+ perc95,
40
+ perc99,
41
+ }
42
+ }
43
+
44
+ let counter = 0
45
+ let latencies = {}
46
+
47
+ const { values } = parseArgs({
48
+ options: {
49
+ qos: {
50
+ type: 'string',
51
+ default: '0',
52
+ choices: ['0', '1'],
53
+ description: 'QoS level to use for publishing messages',
54
+ short: 'q'
55
+ },
56
+ help: {
57
+ type: 'boolean',
58
+ default: false,
59
+ description: 'Show this help message',
60
+ short: 'h'
61
+ }
62
+ }
63
+ })
64
+
65
+ if (values.help) {
66
+ console.log('Usage: node pingpong.js [options]')
67
+ console.log('Options:')
68
+ console.log(' -q, --qos <0|1> QoS level to use for publishing messages (default: 0)')
69
+ console.log(' -h, --help Show this help message')
70
+ process.exit(0)
71
+ }
72
+
73
+ if (!process.send) {
74
+ console.error(`Starting pingpong with options: qos=${values.qos}`)
75
+ }
76
+ const client = mqtt.connect({ port: 1883, host: 'localhost', clean: true, encoding: 'binary', keepalive: 0 })
77
+
78
+ const qosOpts = {
79
+ qos: parseInt(values.qos, 10),
80
+ }
11
81
 
12
82
  function count () {
13
- console.log('sent/s', sent / interval * 1000)
14
- sent = 0
83
+ // reset latencies while keeping the counts
84
+
85
+ const latencyResult = processsLatencies(latencies, counter)
86
+ latencies = {}
87
+ if (process.send) {
88
+ process.send({ type: 'latency', data: latencyResult })
89
+ } else {
90
+ console.log('latencies', latencyResult)
91
+ }
92
+ counter = 0
15
93
  }
16
94
 
17
95
  setInterval(count, interval)
18
96
 
19
97
  function publish () {
20
- sent++
21
- client.publish('test', JSON.stringify(process.hrtime()), { qos: 1 })
98
+ counter++
99
+ client.publish('test', process.hrtime.bigint().toString(), qosOpts)
22
100
  }
23
101
 
24
102
  function subscribe () {
25
- client.subscribe('test', { qos: 1 }, publish)
103
+ client.subscribe('test', qosOpts, publish)
26
104
  }
27
105
 
28
106
  client.on('connect', subscribe)
29
107
  client.on('message', publish)
30
108
  client.on('message', function (topic, payload) {
31
- const sentAt = JSON.parse(payload)
32
- const diff = process.hrtime(sentAt)
33
- latencies.push(convertHrtime(diff).ms)
109
+ const receivedAt = hrtime.bigint()
110
+ const sentAt = BigInt(payload.toString())
111
+ const msDiff = Math.floor(Number(receivedAt - sentAt) / 1e6) // Convert from nanoseconds to milliseconds
112
+ latencies[msDiff] = (latencies[msDiff] || 0) + 1
34
113
  })
35
114
 
36
115
  client.on('offline', function () {
@@ -41,13 +120,3 @@ client.on('error', function () {
41
120
  console.log('reconnect!')
42
121
  client.stream.end()
43
122
  })
44
-
45
- process.on('SIGINT', function () {
46
- const total = latencies.reduce(function (acc, num) {
47
- return acc + num
48
- })
49
- console.log('total', total)
50
- console.log('average', total / latencies.length)
51
- console.log('mode', mode(latencies))
52
- process.exit(0)
53
- })
@@ -0,0 +1,77 @@
1
+ #! /usr/bin/env node
2
+ import { parseArgs } from 'node:util'
3
+ import mqtt from 'mqtt'
4
+ const interval = 5000
5
+
6
+ let counter = 0
7
+ let previousSerial
8
+
9
+ const { values } = parseArgs({
10
+ options: {
11
+ qos: {
12
+ type: 'string',
13
+ default: '0',
14
+ choices: ['0', '1'],
15
+ description: 'QoS level to use for publishing messages',
16
+ short: 'q'
17
+ },
18
+ help: {
19
+ type: 'boolean',
20
+ default: false,
21
+ description: 'Show this help message',
22
+ short: 'h'
23
+ }
24
+ }
25
+ })
26
+
27
+ if (values.help) {
28
+ console.log('Usage: node receiver.js [options]')
29
+ console.log('Options:')
30
+ console.log(' -q, --qos <0|1> QoS level to use for publishing messages (default: 0)')
31
+ console.log(' -h, --help Show this help message')
32
+ process.exit(0)
33
+ }
34
+
35
+ const client = mqtt.connect({ port: 1883, host: 'localhost', clean: true, encoding: 'binary', keepalive: 0 })
36
+
37
+ if (!process.send) {
38
+ console.error(`Starting receiver with options: qos=${values.qos}`)
39
+ }
40
+ const subscribeOpts = {
41
+ qos: parseInt(values.qos, 10),
42
+ }
43
+
44
+ function count () {
45
+ if (process.send) {
46
+ const rate = Math.floor(counter / interval * 1000)
47
+ process.send({ type: 'rate', data: rate })
48
+ } else {
49
+ console.log('received/s', counter / interval * 1000)
50
+ }
51
+ counter = 0
52
+ }
53
+
54
+ setInterval(count, interval)
55
+
56
+ client.on('connect', function () {
57
+ this.subscribe('test', subscribeOpts)
58
+ })
59
+
60
+ client.handleMessage = function (packet, done) {
61
+ const serial = BigInt(packet.payload.toString())
62
+ if (previousSerial !== undefined && (serial !== (previousSerial + 1n))) {
63
+ console.error(`Received out of order message: expected ${previousSerial}, got ${serial}`)
64
+ }
65
+ previousSerial = serial
66
+ counter++
67
+ done()
68
+ }
69
+
70
+ client.on('offline', function () {
71
+ console.log('offline')
72
+ })
73
+
74
+ client.on('error', function () {
75
+ console.log('reconnect!')
76
+ client.stream.end()
77
+ })
@@ -0,0 +1,150 @@
1
+ #! /usr/bin/env node
2
+
3
+ import readline from 'readline'
4
+ // if any average is more than threshold% off we exit with 1
5
+ const threshold = 10
6
+ let failed = false
7
+
8
+ const defaultUnit = 'msg/s' // default unit for the results
9
+ const units = {
10
+ 'pingpong.js': 'ms'
11
+ }
12
+
13
+ function parseConfig (config) {
14
+ // parse config string like "QoS=0, Cores=2" into an object
15
+ const configObj = {}
16
+ const parts = config.split(',').map(part => part.trim())
17
+ for (const part of parts) {
18
+ const [key, value] = part.split('=').map(s => s.trim())
19
+ if (key && value) {
20
+ configObj[key] = value
21
+ }
22
+ }
23
+ return configObj
24
+ }
25
+
26
+ async function gatherData () {
27
+ // read CSV data from STDIN
28
+ const results = {}
29
+ let maxCounts = 0
30
+ const rl = readline.createInterface({
31
+ input: process.stdin
32
+ })
33
+ // split each line by comma but retain commas inside quotes
34
+ for await (const line of rl) {
35
+ const fields = line.match(/(".*?"|[^",\s]+)(?=\s*,|\s*$)/g).map(s => s.replace(/^"|"$/g, ''))
36
+ const label = fields[0]
37
+ if (!label) {
38
+ continue // skips empty lines
39
+ }
40
+ const benchmark = fields[1]
41
+ const config = fields[2]
42
+ const parsedConfig = parseConfig(config)
43
+ const value = Number(fields[3])
44
+ const key = `${benchmark} QoS${parsedConfig.QoS}`
45
+ if (!results[label]) {
46
+ results[label] = {}
47
+ }
48
+ const resultsL2 = results[label]
49
+ if (!resultsL2[key]) {
50
+ resultsL2[key] = {
51
+ values: [],
52
+ benchmark,
53
+ config,
54
+ unit: units[benchmark] || defaultUnit
55
+ }
56
+ }
57
+ const resultsL3 = resultsL2[key]
58
+ resultsL3.values.push(value)
59
+ if (resultsL3.values.length > maxCounts) {
60
+ maxCounts = resultsL3.values.length
61
+ }
62
+ }
63
+ return { results, maxCounts }
64
+ }
65
+
66
+ function reportPerLabel (label, results, maxCounts, avg) {
67
+ const roundLabels = []
68
+ for (let i = 0; i < maxCounts; i++) {
69
+ roundLabels.push(`Round ${i + 1}`)
70
+ }
71
+
72
+ console.log(`\n # Benchmark Results for ${label}`)
73
+ console.log(`|Benchmark | Config | Units | ${roundLabels.join(' |')}`)
74
+ console.log(`|----------|--------|-------|${roundLabels.map(() => '---').join('|')}`)
75
+ for (const key in results) {
76
+ const { unit, values, benchmark, config } = results[key]
77
+ console.log(`| ${benchmark} | ${config} | ${unit}| ${values.join(' |')}`)
78
+ }
79
+ console.log('\n')
80
+ }
81
+
82
+ function calculateAverages (results) {
83
+ const avg = {}
84
+ for (const label in results) {
85
+ const resultsL2 = results[label]
86
+ for (const key in resultsL2) {
87
+ const { unit, values, benchmark, config } = resultsL2[key]
88
+ if (!avg[key]) {
89
+ avg[key] = {}
90
+ }
91
+ avg[key][label] = {
92
+ value: values.reduce((acc, num) => acc + num, 0) / values.length,
93
+ benchmark,
94
+ unit,
95
+ config
96
+ }
97
+ }
98
+ }
99
+ return avg
100
+ }
101
+
102
+ function calculatePercentage (ref, value) {
103
+ if (ref === undefined) {
104
+ return { ref: value, diff: 0 }
105
+ }
106
+ const perc = ((value / ref) * 100)
107
+ if (perc > 100) {
108
+ const diff = perc - 100
109
+ return { ref, diff }
110
+ }
111
+ const diff = (100 - perc) * -1
112
+ return { ref, diff }
113
+ }
114
+
115
+ function reportAverages (avg) {
116
+ console.log('\n # Overall Benchmark Results')
117
+ console.log(`\n +x% is better, -x% is worse, current threshold to fail at -${threshold}%\n\n`)
118
+ console.log('| Label | Benchmark | Config | Average | Units | Percentage')
119
+ console.log('|-------|-----------|--------|---------|-------|-----------')
120
+ for (const key in avg) {
121
+ let oldRef
122
+ for (const label in avg[key]) {
123
+ const { value, unit, benchmark, config } = avg[key][label]
124
+ const { ref, diff } = calculatePercentage(oldRef, value)
125
+ oldRef = ref
126
+ // for unit = ms lower is better
127
+ const correctedDiff = unit === 'ms' ? diff * -1 : diff
128
+ const sign = correctedDiff > 0 ? '+' : ''
129
+ const perc = correctedDiff === 0 ? 100 : `${sign}${correctedDiff.toFixed(2)}`
130
+ if ((-1 * correctedDiff) > threshold) {
131
+ console.error(`\n\nError: ${key} is ${sign}${correctedDiff.toFixed(2)}% off the reference (${ref} ${unit}) which is more than the threshold of ${threshold}% `)
132
+ failed = true
133
+ }
134
+ console.log(`| ${label} | ${benchmark} | ${config} | ${value.toFixed(0)} | ${unit} | ${perc}%`)
135
+ }
136
+ }
137
+ }
138
+
139
+ async function report () {
140
+ const { results, maxCounts } = await gatherData()
141
+ const avg = calculateAverages(results)
142
+ reportAverages(avg)
143
+ for (const label in results) {
144
+ reportPerLabel(label, results[label], maxCounts)
145
+ }
146
+ }
147
+
148
+ report().then(() => {
149
+ process.exit(failed ? 1 : 0)
150
+ })
@@ -0,0 +1,118 @@
1
+ import { fork, execSync } from 'node:child_process'
2
+ import { cpus } from 'node:os'
3
+ import path from 'node:path'
4
+ const gitBranch = execSync('git rev-parse --abbrev-ref HEAD').toString().trim()
5
+ const numCores = cpus().length
6
+ const cpuType = cpus()[0].model.trim()
7
+ const dirname = import.meta.dirname
8
+
9
+ const scripts = {
10
+ server: path.join(dirname, 'server.js'),
11
+ sender: path.join(dirname, 'sender.js'),
12
+ receiver: path.join(dirname, 'receiver.js'),
13
+ pingpong: path.join(dirname, 'pingpong.js'),
14
+ }
15
+
16
+ function spawn (script, cmdArgs = [], msgType = 'rate', args = {}) {
17
+ const child = fork(script, cmdArgs, { env: { ...process.env, ...args } })
18
+ const results = []
19
+ child.on('message', msg => {
20
+ if (msg.type === msgType) results.push(msg.data)
21
+ })
22
+ return { child, results }
23
+ }
24
+
25
+ async function throughputTest (scripts, qos, warmupCount, maxCount) {
26
+ console.error('Starting test for QoS', qos)
27
+ await new Promise(resolve => {
28
+ console.error(`Starting warmup for QoS${qos}`)
29
+ // 1. Start server (no rates to collect)
30
+ const server = fork(scripts.server)
31
+
32
+ // 2. Start throughput receiver
33
+ const receiver = spawn(scripts.receiver, [`-q ${qos}`], 'rate')
34
+
35
+ // 3. Start throughput sender
36
+ const sender = spawn(scripts.sender, [`-q ${qos}`], 'rate')
37
+ const config = `"QoS=${qos}, Cores=${numCores}"`
38
+ sender.child.on('message', checkDone)
39
+
40
+ // 4. Collect and print rates
41
+ let counter = 0
42
+ function checkDone () {
43
+ counter++
44
+ process.stderr.write('.')
45
+ if (counter === warmupCount) {
46
+ console.error('\n starting measurement')
47
+ receiver.results.length = 0
48
+ sender.results.length = 0
49
+ }
50
+ if (counter === (maxCount + warmupCount)) {
51
+ sender.child.kill()
52
+ receiver.child.kill()
53
+ server.kill()
54
+ for (const result of sender.results) {
55
+ console.log(`${gitBranch}, sender.js,${config} , ${result}`)
56
+ }
57
+ for (const result of receiver.results) {
58
+ console.log(`${gitBranch}, receiver.js,${config} , ${result}`)
59
+ }
60
+ resolve()
61
+ }
62
+ }
63
+ })
64
+ }
65
+ async function latencyTest (scripts, qos, warmupCount, maxCount, score) {
66
+ console.error('Starting latency test for QoS', qos)
67
+ await new Promise(resolve => {
68
+ console.error(`Starting warmup for QoS${qos}`)
69
+ // 1. Start server (no rates to collect)
70
+ const server = fork(scripts.server)
71
+
72
+ // 2. Start latency measurement
73
+ const pingpong = spawn(scripts.pingpong, [`-q ${qos}`], 'latency')
74
+ const config = `"QoS=${qos}, Cores=${numCores}, Score='${score}'"`
75
+ pingpong.child.on('message', checkDone)
76
+
77
+ // 4. Collect and print latency
78
+ let counter = 0
79
+ function checkDone () {
80
+ counter++
81
+ process.stderr.write('.')
82
+ if (counter === warmupCount) {
83
+ console.error('\n starting measurement')
84
+ pingpong.results.length = 0
85
+ }
86
+ if (counter === (maxCount + warmupCount)) {
87
+ pingpong.child.kill()
88
+ server.kill()
89
+ for (const result of pingpong.results) {
90
+ console.log(`${gitBranch}, pingpong.js ,${config} , ${result[score]}`)
91
+ }
92
+ resolve()
93
+ }
94
+ }
95
+ })
96
+ }
97
+
98
+ async function main (opts) {
99
+ console.error(`Running benchmarks on branch: ${gitBranch} using ${cpuType} with ${numCores} cores`)
100
+ if (numCores < 3) {
101
+ console.error('WARNING: Not enough CPU cores to run proper benchmarks, at least 4 cores are recommended')
102
+ }
103
+ await throughputTest(scripts, 0, opts.warmupCount, opts.maxCount)
104
+ await throughputTest(scripts, 1, opts.warmupCount, opts.maxCount)
105
+ await latencyTest(scripts, 1, opts.warmupCount, opts.maxCount, opts.latencyScore)
106
+ console.error('Tests done')
107
+ process.exit(0)
108
+ }
109
+
110
+ const defaultopts = {
111
+ warmupCount: 5,
112
+ maxCount: 10,
113
+ latencyScore: 'perc95' // valid score names are mean, median, perc95 and perc99
114
+ }
115
+ main(defaultopts).catch(err => {
116
+ console.error('Error in main:', err)
117
+ process.exit(1)
118
+ })
@@ -0,0 +1,86 @@
1
+ #! /usr/bin/env node
2
+
3
+ import mqtt from 'mqtt'
4
+ import { parseArgs } from 'node:util'
5
+ const interval = 5000
6
+
7
+ let sent = 0
8
+ let serial = 0n
9
+
10
+ const { values } = parseArgs({
11
+ options: {
12
+ qos: {
13
+ type: 'string',
14
+ default: '0',
15
+ choices: ['0', '1'],
16
+ description: 'QoS level to use for publishing messages',
17
+ short: 'q'
18
+ },
19
+ help: {
20
+ type: 'boolean',
21
+ default: false,
22
+ description: 'Show this help message',
23
+ short: 'h'
24
+ }
25
+ }
26
+ })
27
+
28
+ if (values.help) {
29
+ console.log('Usage: node sender.js [options]')
30
+ console.log('Options:')
31
+ console.log(' -q, --qos <0|1> QoS level to use for publishing messages (default: 0)')
32
+ console.log(' -h, --help Show this help message')
33
+ process.exit(0)
34
+ }
35
+
36
+ if (!process.send) {
37
+ console.error(`Starting sender with options: qos=${values.qos}`)
38
+ }
39
+ const publishOpts = {
40
+ qos: parseInt(values.qos, 10),
41
+ retain: false,
42
+ dup: false
43
+ }
44
+
45
+ const client = mqtt.connect({ port: 1883, host: 'localhost', clean: true, keepalive: 0 })
46
+
47
+ function count () {
48
+ if (process.send) {
49
+ const rate = Math.floor(sent / interval * 1000)
50
+ process.send({ type: 'rate', data: rate })
51
+ } else {
52
+ console.log('sent/s', sent / interval * 1000)
53
+ }
54
+ sent = 0
55
+ }
56
+
57
+ setInterval(count, interval)
58
+
59
+ function immediatePublish () {
60
+ setImmediate(publish)
61
+ }
62
+
63
+ function publish () {
64
+ sent++
65
+ serial++
66
+ const payload = serial.toString()
67
+
68
+ client.publish('test', payload, publishOpts, immediatePublish)
69
+ }
70
+
71
+ client.setMaxListeners(100)
72
+
73
+ client.on('connect', function () {
74
+ for (let i = 0; i < 50; i++) {
75
+ publish()
76
+ }
77
+ })
78
+
79
+ client.on('offline', function () {
80
+ console.log('offline')
81
+ })
82
+
83
+ client.on('error', function () {
84
+ console.log('reconnect!')
85
+ client.stream.end()
86
+ })
@@ -1,25 +1,26 @@
1
- 'use strict'
1
+ import { Aedes } from '../aedes.js'
2
+ import { createServer } from 'net'
2
3
 
3
4
  // To be used with cpuprofilify http://npm.im/cpuprofilify
5
+ Aedes.createBroker().then(aedes => {
6
+ const server = createServer(aedes.handle)
7
+ const port = 1883
4
8
 
5
- const aedes = require('../')()
6
- const server = require('net').createServer(aedes.handle)
7
- const port = 1883
9
+ server.listen(port, function () {
10
+ console.error('server listening on port', port, 'pid', process.pid)
11
+ })
8
12
 
9
- server.listen(port, function () {
10
- console.error('server listening on port', port, 'pid', process.pid)
11
- })
12
-
13
- aedes.on('clientError', function (client, err) {
14
- console.error('client error', client.id, err.message)
15
- })
13
+ aedes.on('clientError', function (client, err) {
14
+ console.error('client error', client.id, err.message)
15
+ })
16
16
 
17
- // Cleanly shut down process on SIGTERM to ensure that perf-<pid>.map gets flushed
18
- process.on('SIGINT', onSIGINT)
17
+ // Cleanly shut down process on SIGTERM to ensure that perf-<pid>.map gets flushed
18
+ process.on('SIGINT', onSIGINT)
19
19
 
20
- function onSIGINT () {
20
+ function onSIGINT () {
21
21
  // IMPORTANT to log on stderr, to not clutter stdout which is purely for data, i.e. dtrace stacks
22
- console.error('Caught SIGTERM, shutting down.')
23
- server.close()
24
- process.exit(0)
25
- }
22
+ console.error('Caught SIGTERM, shutting down.')
23
+ server.close()
24
+ process.exit(0)
25
+ }
26
+ })
@@ -0,0 +1,20 @@
1
+ // node 20 on windows has issues with node:test wildcard
2
+ // this script is only used by package.json
3
+ // example of usage in package.json:
4
+ // "unit": "node checkVersion.js && npm run unit:v20win32 || npm run unit:other",
5
+ // "unit:v20win32": "node --test --test-timeout=180000",
6
+ // "unit:other": "node --test --test-timeout=180000 test/*.js test/*.cjs",
7
+
8
+ /* c8 ignore start */
9
+ import { platform, version, exit } from 'node:process'
10
+
11
+ const major = version.split('.')[0]
12
+ let exitCode = 1
13
+ // node 20 on windows returns 0
14
+ if ((major === 'v20') && platform === 'win32') {
15
+ exitCode = 0
16
+ }
17
+ // all the others 1
18
+ console.log(`Running ${major} on ${platform} returning exitCode ${exitCode}`)
19
+ exit(exitCode)
20
+ /* c8 ignore stop */