thread-stream 3.1.0 → 4.0.0

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.
@@ -24,7 +24,7 @@ jobs:
24
24
  contents: read
25
25
  steps:
26
26
  - name: Check out repo
27
- uses: actions/checkout@v3
27
+ uses: actions/checkout@v4
28
28
  with:
29
29
  persist-credentials: false
30
30
 
@@ -38,15 +38,12 @@ jobs:
38
38
  contents: read
39
39
  strategy:
40
40
  matrix:
41
- node-version: [18, 20, 22]
41
+ node-version: [20, 22, 24]
42
42
  os: [macos-latest, ubuntu-latest, windows-latest]
43
- exclude:
44
- - os: windows-latest
45
- node-version: 22
46
43
 
47
44
  steps:
48
45
  - name: Check out repo
49
- uses: actions/checkout@v3
46
+ uses: actions/checkout@v4
50
47
  with:
51
48
  persist-credentials: false
52
49
 
@@ -61,23 +58,6 @@ jobs:
61
58
  - name: Run tests
62
59
  run: npm run test:ci
63
60
 
64
- - name: Coveralls Parallel
65
- uses: coverallsapp/github-action@v2.3.0
66
- with:
67
- github-token: ${{ secrets.github_token }}
68
- parallel: true
69
- flag-name: run-${{ matrix.node-version }}-${{ matrix.os }}
70
-
71
- coverage:
72
- needs: test
73
- runs-on: ubuntu-latest
74
- steps:
75
- - name: Coveralls Finished
76
- uses: coverallsapp/github-action@v2.3.0
77
- with:
78
- github-token: ${{ secrets.GITHUB_TOKEN }}
79
- parallel-finished: true
80
-
81
61
  automerge:
82
62
  name: Automerge Dependabot PRs
83
63
  if: >
package/CLAUDE.md ADDED
@@ -0,0 +1,53 @@
1
+ # CLAUDE.md
2
+
3
+ This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4
+
5
+ ## Build & Test Commands
6
+
7
+ ```sh
8
+ npm test # Run linter (standard), type check, transpile, and all tests
9
+ npm run build # Type check only (tsc --noEmit)
10
+ npm run transpile # Generate transpiled test files for various ES targets
11
+
12
+ # Run a single test file
13
+ npx tap test/base.test.js
14
+
15
+ # Run only JavaScript tests (faster)
16
+ npx tap "test/**/*.test.*js"
17
+
18
+ # Run only TypeScript tests
19
+ npx tap --ts test/*.test.*ts
20
+ ```
21
+
22
+ ## Code Style
23
+
24
+ This project uses [Standard](https://standardjs.com/) for linting. No semicolons, 2-space indentation.
25
+
26
+ ## Architecture
27
+
28
+ thread-stream is a library for streaming data to a Node.js Worker Thread using SharedArrayBuffer for high-performance inter-thread communication.
29
+
30
+ ### Core Components
31
+
32
+ - **index.js** - Main `ThreadStream` class extending EventEmitter. Manages the worker lifecycle, handles writes via SharedArrayBuffer, and coordinates synchronization using Atomics.
33
+
34
+ - **lib/worker.js** - Runs in the Worker Thread. Loads the user-provided destination module, reads data from SharedArrayBuffer, and writes to the destination stream. Handles both ESM and CommonJS modules, including yarn PnP compatibility.
35
+
36
+ - **lib/indexes.js** - Defines SharedArrayBuffer index constants (`WRITE_INDEX`, `READ_INDEX`) used for Atomics coordination.
37
+
38
+ - **lib/wait.js** - Async polling utilities (`wait`, `waitDiff`) for cross-thread synchronization without blocking the main thread.
39
+
40
+ ### Data Flow
41
+
42
+ 1. Main thread writes strings to an internal buffer, then copies to SharedArrayBuffer
43
+ 2. Atomics.notify signals the worker that data is available
44
+ 3. Worker reads from SharedArrayBuffer via Atomics.load and writes to destination stream
45
+ 4. Worker updates READ_INDEX and notifies main thread when done
46
+ 5. Special index values (-1 for end, -2 for error) signal stream termination
47
+
48
+ ### Key Features
49
+
50
+ - **Sync/async modes** - `sync: true` blocks until data is written; async mode uses `setImmediate` batching
51
+ - **Backpressure** - Handles `drain` events from destination streams
52
+ - **GC cleanup** - Uses FinalizationRegistry to terminate orphaned workers
53
+ - **TypeScript support** - Workers can be `.ts` files (requires ts-node)
@@ -0,0 +1,10 @@
1
+ 'use strict'
2
+
3
+ const neostandard = require('neostandard')
4
+
5
+ module.exports = neostandard({
6
+ ignores: [
7
+ 'test/ts/**/*',
8
+ 'test/syntax-error.mjs'
9
+ ]
10
+ })
package/index.js CHANGED
@@ -122,6 +122,7 @@ function nextFlush (stream) {
122
122
 
123
123
  Atomics.store(stream[kImpl].state, READ_INDEX, 0)
124
124
  Atomics.store(stream[kImpl].state, WRITE_INDEX, 0)
125
+ Atomics.notify(stream[kImpl].state, READ_INDEX)
125
126
 
126
127
  // Find a toWrite length that fits the buffer
127
128
  // it must exists as the buffer is at least 4 bytes length
@@ -143,6 +144,7 @@ function nextFlush (stream) {
143
144
  stream.flush(() => {
144
145
  Atomics.store(stream[kImpl].state, READ_INDEX, 0)
145
146
  Atomics.store(stream[kImpl].state, WRITE_INDEX, 0)
147
+ Atomics.notify(stream[kImpl].state, READ_INDEX)
146
148
  nextFlush(stream)
147
149
  })
148
150
  } else {
@@ -468,6 +470,7 @@ function writeSync (stream) {
468
470
  flushSync(stream)
469
471
  Atomics.store(stream[kImpl].state, READ_INDEX, 0)
470
472
  Atomics.store(stream[kImpl].state, WRITE_INDEX, 0)
473
+ Atomics.notify(stream[kImpl].state, READ_INDEX)
471
474
  continue
472
475
  } else if (leftover < 0) {
473
476
  // stream should never happen
@@ -485,6 +488,7 @@ function writeSync (stream) {
485
488
  flushSync(stream)
486
489
  Atomics.store(stream[kImpl].state, READ_INDEX, 0)
487
490
  Atomics.store(stream[kImpl].state, WRITE_INDEX, 0)
491
+ Atomics.notify(stream[kImpl].state, READ_INDEX)
488
492
 
489
493
  // Find a toWrite length that fits the buffer
490
494
  // it must exists as the buffer is at least 4 bytes length
package/lib/wait.js CHANGED
@@ -1,61 +1,68 @@
1
1
  'use strict'
2
2
 
3
- const MAX_TIMEOUT = 1000
3
+ // Maximum wait time for a single waitAsync call
4
+ // Used as a fallback poll interval in case notifications are missed
5
+ // Keep this low enough for good throughput but high enough to not busy-loop
6
+ const WAIT_MS = 10000
4
7
 
5
8
  function wait (state, index, expected, timeout, done) {
6
- const max = Date.now() + timeout
7
- let current = Atomics.load(state, index)
8
- if (current === expected) {
9
- done(null, 'ok')
10
- return
11
- }
12
- let prior = current
13
- const check = (backoff) => {
14
- if (Date.now() > max) {
9
+ const max = timeout === Infinity ? Infinity : Date.now() + timeout
10
+
11
+ const check = () => {
12
+ const current = Atomics.load(state, index)
13
+ if (current === expected) {
14
+ done(null, 'ok')
15
+ return
16
+ }
17
+
18
+ if (max !== Infinity && Date.now() > max) {
15
19
  done(null, 'timed-out')
20
+ return
21
+ }
22
+
23
+ // Wait for any change from current value
24
+ const remaining = max === Infinity ? WAIT_MS : Math.min(WAIT_MS, Math.max(1, max - Date.now()))
25
+ const result = Atomics.waitAsync(state, index, current, remaining)
26
+
27
+ if (result.async) {
28
+ result.value.then(check)
16
29
  } else {
17
- setTimeout(() => {
18
- prior = current
19
- current = Atomics.load(state, index)
20
- if (current === prior) {
21
- check(backoff >= MAX_TIMEOUT ? MAX_TIMEOUT : backoff * 2)
22
- } else {
23
- if (current === expected) done(null, 'ok')
24
- else done(null, 'not-equal')
25
- }
26
- }, backoff)
30
+ // Value already changed (not-equal) - recheck on next tick
31
+ setImmediate(check)
27
32
  }
28
33
  }
29
- check(1)
34
+
35
+ check()
30
36
  }
31
37
 
32
- // let waitDiffCount = 0
33
38
  function waitDiff (state, index, expected, timeout, done) {
34
- // const id = waitDiffCount++
35
- // process._rawDebug(`>>> waitDiff ${id}`)
36
- const max = Date.now() + timeout
37
- let current = Atomics.load(state, index)
38
- if (current !== expected) {
39
- done(null, 'ok')
40
- return
41
- }
42
- const check = (backoff) => {
43
- // process._rawDebug(`${id} ${index} current ${current} expected ${expected}`)
44
- // process._rawDebug('' + backoff)
45
- if (Date.now() > max) {
39
+ const max = timeout === Infinity ? Infinity : Date.now() + timeout
40
+
41
+ const check = () => {
42
+ const current = Atomics.load(state, index)
43
+ if (current !== expected) {
44
+ done(null, 'ok')
45
+ return
46
+ }
47
+
48
+ if (max !== Infinity && Date.now() > max) {
46
49
  done(null, 'timed-out')
50
+ return
51
+ }
52
+
53
+ // Wait for value to change from expected
54
+ const remaining = max === Infinity ? WAIT_MS : Math.min(WAIT_MS, Math.max(1, max - Date.now()))
55
+ const result = Atomics.waitAsync(state, index, expected, remaining)
56
+
57
+ if (result.async) {
58
+ result.value.then(check)
47
59
  } else {
48
- setTimeout(() => {
49
- current = Atomics.load(state, index)
50
- if (current !== expected) {
51
- done(null, 'ok')
52
- } else {
53
- check(backoff >= MAX_TIMEOUT ? MAX_TIMEOUT : backoff * 2)
54
- }
55
- }, backoff)
60
+ // Value already changed (not-equal) - recheck on next tick
61
+ setImmediate(check)
56
62
  }
57
63
  }
58
- check(1)
64
+
65
+ check()
59
66
  }
60
67
 
61
68
  module.exports = { wait, waitDiff }
package/lib/worker.js CHANGED
@@ -16,22 +16,13 @@ let destination
16
16
  const state = new Int32Array(stateBuf)
17
17
  const data = Buffer.from(dataBuf)
18
18
 
19
+ // Keep the event loop alive - Atomics.waitAsync promises don't prevent worker exit
20
+ const keepAlive = setInterval(() => {}, 60 * 60 * 1000)
21
+
19
22
  async function start () {
20
23
  let worker
21
24
  try {
22
- if (filename.endsWith('.ts') || filename.endsWith('.cts')) {
23
- // TODO: add support for the TSM modules loader ( https://github.com/lukeed/tsm ).
24
- if (!process[Symbol.for('ts-node.register.instance')]) {
25
- realRequire('ts-node/register')
26
- } else if (process.env.TS_NODE_DEV) {
27
- realRequire('ts-node-dev')
28
- }
29
- // TODO: Support ES imports once tsc, tap & ts-node provide better compatibility guarantees.
30
- // Remove extra forwardslash on Windows
31
- worker = realRequire(decodeURIComponent(filename.replace(process.platform === 'win32' ? 'file:///' : 'file://', '')))
32
- } else {
33
- worker = (await realImport(filename))
34
- }
25
+ worker = (await realImport(filename))
35
26
  } catch (error) {
36
27
  // A yarn user that tries to start a ThreadStream for an external module
37
28
  // provides a filename pointing to a zip file.
@@ -53,6 +44,19 @@ async function start () {
53
44
  } catch {
54
45
  throw error
55
46
  }
47
+ } else if (filename.endsWith('.ts') || filename.endsWith('.cts')) {
48
+ // Native TypeScript import failed (type stripping not enabled).
49
+ // Fall back to ts-node for TypeScript files.
50
+ try {
51
+ if (!process[Symbol.for('ts-node.register.instance')]) {
52
+ realRequire('ts-node/register')
53
+ } else if (process.env.TS_NODE_DEV) {
54
+ realRequire('ts-node-dev')
55
+ }
56
+ worker = realRequire(decodeURIComponent(filename.replace(process.platform === 'win32' ? 'file:///' : 'file://', '')))
57
+ } catch {
58
+ throw error
59
+ }
56
60
  } else {
57
61
  throw error
58
62
  }
@@ -84,6 +88,7 @@ async function start () {
84
88
  const end = Atomics.load(state, WRITE_INDEX)
85
89
  Atomics.store(state, READ_INDEX, end)
86
90
  Atomics.notify(state, READ_INDEX)
91
+ clearInterval(keepAlive)
87
92
  setImmediate(() => {
88
93
  process.exit(0)
89
94
  })
package/package.json CHANGED
@@ -1,43 +1,38 @@
1
1
  {
2
2
  "name": "thread-stream",
3
- "version": "3.1.0",
3
+ "version": "4.0.0",
4
4
  "description": "A streaming way to send data to a Node.js Worker Thread",
5
5
  "main": "index.js",
6
6
  "types": "index.d.ts",
7
+ "engines": {
8
+ "node": ">=20"
9
+ },
7
10
  "dependencies": {
8
11
  "real-require": "^0.2.0"
9
12
  },
10
13
  "devDependencies": {
11
- "@types/node": "^20.1.0",
12
- "@types/tap": "^15.0.0",
13
- "@yao-pkg/pkg": "^5.11.5",
14
+ "@types/node": "^22.0.0",
15
+ "@yao-pkg/pkg": "^6.0.0",
16
+ "borp": "^0.21.0",
14
17
  "desm": "^1.3.0",
18
+ "eslint": "^9.39.1",
15
19
  "fastbench": "^1.0.1",
16
20
  "husky": "^9.0.6",
21
+ "neostandard": "^0.12.2",
17
22
  "pino-elasticsearch": "^8.0.0",
18
23
  "sonic-boom": "^4.0.1",
19
- "standard": "^17.0.0",
20
- "tap": "^16.2.0",
21
24
  "ts-node": "^10.8.0",
22
- "typescript": "^5.3.2",
23
- "why-is-node-running": "^2.2.2"
25
+ "typescript": "~5.7.3"
24
26
  },
25
27
  "scripts": {
26
28
  "build": "tsc --noEmit",
27
- "test": "standard && npm run build && npm run transpile && tap \"test/**/*.test.*js\" && tap --ts test/*.test.*ts",
28
- "test:ci": "standard && npm run transpile && npm run test:ci:js && npm run test:ci:ts",
29
- "test:ci:js": "tap --no-check-coverage --timeout=120 --coverage-report=lcovonly \"test/**/*.test.*js\"",
30
- "test:ci:ts": "tap --ts --no-check-coverage --coverage-report=lcovonly \"test/**/*.test.*ts\"",
31
- "test:yarn": "npm run transpile && tap \"test/**/*.test.js\" --no-check-coverage",
29
+ "lint": "eslint",
30
+ "test": "npm run lint && npm run build && npm run transpile && borp --pattern 'test/*.test.{js,mjs}'",
31
+ "test:ci": "npm run lint && npm run transpile && borp --pattern 'test/*.test.{js,mjs}'",
32
+ "test:yarn": "npm run transpile && borp --pattern 'test/*.test.js'",
32
33
  "transpile": "sh ./test/ts/transpile.sh",
33
34
  "prepare": "husky install"
34
35
  },
35
- "standard": {
36
- "ignore": [
37
- "test/ts/**/*",
38
- "test/syntax-error.mjs"
39
- ]
40
- },
41
36
  "repository": {
42
37
  "type": "git",
43
38
  "url": "git+https://github.com/mcollina/thread-stream.git"
package/test/base.test.js CHANGED
@@ -1,6 +1,7 @@
1
1
  'use strict'
2
2
 
3
- const { test } = require('tap')
3
+ const { test } = require('node:test')
4
+ const assert = require('node:assert')
4
5
  const { join } = require('path')
5
6
  const { readFile } = require('fs')
6
7
  const { file } = require('./helper')
@@ -8,9 +9,7 @@ const ThreadStream = require('..')
8
9
  const { MessageChannel } = require('worker_threads')
9
10
  const { once } = require('events')
10
11
 
11
- test('base sync=true', function (t) {
12
- t.plan(15)
13
-
12
+ test('base sync=true', function (t, done) {
14
13
  const dest = file()
15
14
  const stream = new ThreadStream({
16
15
  filename: join(__dirname, 'to-file.js'),
@@ -18,37 +17,35 @@ test('base sync=true', function (t) {
18
17
  sync: true
19
18
  })
20
19
 
21
- t.same(stream.writableObjectMode, false)
20
+ assert.deepStrictEqual(stream.writableObjectMode, false)
22
21
 
23
- t.same(stream.writableFinished, false)
22
+ assert.deepStrictEqual(stream.writableFinished, false)
24
23
  stream.on('finish', () => {
25
- t.same(stream.writableFinished, true)
24
+ assert.deepStrictEqual(stream.writableFinished, true)
26
25
  readFile(dest, 'utf8', (err, data) => {
27
- t.error(err)
28
- t.equal(data, 'hello world\nsomething else\n')
26
+ assert.ifError(err)
27
+ assert.strictEqual(data, 'hello world\nsomething else\n')
29
28
  })
30
29
  })
31
30
 
32
- t.same(stream.closed, false)
31
+ assert.deepStrictEqual(stream.closed, false)
33
32
  stream.on('close', () => {
34
- t.same(stream.closed, true)
35
- t.notOk(stream.writable)
36
- t.pass('close emitted')
33
+ assert.deepStrictEqual(stream.closed, true)
34
+ assert.ok(!stream.writable)
35
+ done()
37
36
  })
38
37
 
39
- t.same(stream.writableNeedDrain, false)
40
- t.ok(stream.write('hello world\n'))
41
- t.ok(stream.write('something else\n'))
42
- t.ok(stream.writable)
38
+ assert.deepStrictEqual(stream.writableNeedDrain, false)
39
+ assert.ok(stream.write('hello world\n'))
40
+ assert.ok(stream.write('something else\n'))
41
+ assert.ok(stream.writable)
43
42
 
44
- t.same(stream.writableEnded, false)
43
+ assert.deepStrictEqual(stream.writableEnded, false)
45
44
  stream.end()
46
- t.same(stream.writableEnded, true)
45
+ assert.deepStrictEqual(stream.writableEnded, true)
47
46
  })
48
47
 
49
- test('overflow sync=true', function (t) {
50
- t.plan(3)
51
-
48
+ test('overflow sync=true', function (t, done) {
52
49
  const dest = file()
53
50
  const stream = new ThreadStream({
54
51
  bufferSize: 128,
@@ -73,19 +70,16 @@ test('overflow sync=true', function (t) {
73
70
 
74
71
  write()
75
72
 
76
- stream.on('finish', () => {
77
- t.pass('finish emitted')
78
- })
79
-
80
73
  stream.on('close', () => {
81
74
  readFile(dest, 'utf8', (err, data) => {
82
- t.error(err)
83
- t.equal(data.length, 200)
75
+ assert.ifError(err)
76
+ assert.strictEqual(data.length, 200)
77
+ done()
84
78
  })
85
79
  })
86
80
  })
87
81
 
88
- test('overflow sync=false', function (t) {
82
+ test('overflow sync=false', function (t, done) {
89
83
  const dest = file()
90
84
  const stream = new ThreadStream({
91
85
  bufferSize: 128,
@@ -96,18 +90,17 @@ test('overflow sync=false', function (t) {
96
90
 
97
91
  let count = 0
98
92
 
99
- t.same(stream.writableNeedDrain, false)
93
+ assert.deepStrictEqual(stream.writableNeedDrain, false)
100
94
 
101
95
  // Write 10 chars, 20 times
102
96
  function write () {
103
97
  if (count++ === 20) {
104
- t.pass('end sent')
105
98
  stream.end()
106
99
  return
107
100
  }
108
101
 
109
102
  if (!stream.write('aaaaaaaaaa')) {
110
- t.same(stream.writableNeedDrain, true)
103
+ assert.deepStrictEqual(stream.writableNeedDrain, true)
111
104
  }
112
105
  // do not wait for drain event
113
106
  setImmediate(write)
@@ -116,26 +109,19 @@ test('overflow sync=false', function (t) {
116
109
  write()
117
110
 
118
111
  stream.on('drain', () => {
119
- t.same(stream.writableNeedDrain, false)
120
- t.pass('drain')
121
- })
122
-
123
- stream.on('finish', () => {
124
- t.pass('finish emitted')
112
+ assert.deepStrictEqual(stream.writableNeedDrain, false)
125
113
  })
126
114
 
127
115
  stream.on('close', () => {
128
116
  readFile(dest, 'utf8', (err, data) => {
129
- t.error(err)
130
- t.equal(data.length, 200)
131
- t.end()
117
+ assert.ifError(err)
118
+ assert.strictEqual(data.length, 200)
119
+ done()
132
120
  })
133
121
  })
134
122
  })
135
123
 
136
- test('over the bufferSize at startup', function (t) {
137
- t.plan(6)
138
-
124
+ test('over the bufferSize at startup', function (t, done) {
139
125
  const dest = file()
140
126
  const stream = new ThreadStream({
141
127
  bufferSize: 10,
@@ -146,25 +132,23 @@ test('over the bufferSize at startup', function (t) {
146
132
 
147
133
  stream.on('finish', () => {
148
134
  readFile(dest, 'utf8', (err, data) => {
149
- t.error(err)
150
- t.equal(data, 'hello world\nsomething else\n')
135
+ assert.ifError(err)
136
+ assert.strictEqual(data, 'hello world\nsomething else\n')
151
137
  })
152
138
  })
153
139
 
154
140
  stream.on('close', () => {
155
- t.pass('close emitted')
141
+ done()
156
142
  })
157
143
 
158
- t.ok(stream.write('hello'))
159
- t.ok(stream.write(' world\n'))
160
- t.ok(stream.write('something else\n'))
144
+ assert.ok(stream.write('hello'))
145
+ assert.ok(stream.write(' world\n'))
146
+ assert.ok(stream.write('something else\n'))
161
147
 
162
148
  stream.end()
163
149
  })
164
150
 
165
- test('over the bufferSize at startup (async)', function (t) {
166
- t.plan(6)
167
-
151
+ test('over the bufferSize at startup (async)', function (t, done) {
168
152
  const dest = file()
169
153
  const stream = new ThreadStream({
170
154
  bufferSize: 10,
@@ -173,25 +157,25 @@ test('over the bufferSize at startup (async)', function (t) {
173
157
  sync: false
174
158
  })
175
159
 
176
- t.ok(stream.write('hello'))
177
- t.notOk(stream.write(' world\n'))
178
- t.notOk(stream.write('something else\n'))
160
+ assert.ok(stream.write('hello'))
161
+ assert.ok(!stream.write(' world\n'))
162
+ assert.ok(!stream.write('something else\n'))
179
163
 
180
164
  stream.end()
181
165
 
182
166
  stream.on('finish', () => {
183
167
  readFile(dest, 'utf8', (err, data) => {
184
- t.error(err)
185
- t.equal(data, 'hello world\nsomething else\n')
168
+ assert.ifError(err)
169
+ assert.strictEqual(data, 'hello world\nsomething else\n')
186
170
  })
187
171
  })
188
172
 
189
173
  stream.on('close', () => {
190
- t.pass('close emitted')
174
+ done()
191
175
  })
192
176
  })
193
177
 
194
- test('flushSync sync=false', function (t) {
178
+ test('flushSync sync=false', function (t, done) {
195
179
  const dest = file()
196
180
  const stream = new ThreadStream({
197
181
  bufferSize: 128,
@@ -201,19 +185,14 @@ test('flushSync sync=false', function (t) {
201
185
  })
202
186
 
203
187
  stream.on('drain', () => {
204
- t.pass('drain')
205
188
  stream.end()
206
189
  })
207
190
 
208
- stream.on('finish', () => {
209
- t.pass('finish emitted')
210
- })
211
-
212
191
  stream.on('close', () => {
213
192
  readFile(dest, 'utf8', (err, data) => {
214
- t.error(err)
215
- t.equal(data.length, 200)
216
- t.end()
193
+ assert.ifError(err)
194
+ assert.strictEqual(data.length, 200)
195
+ done()
217
196
  })
218
197
  })
219
198
 
@@ -224,8 +203,6 @@ test('flushSync sync=false', function (t) {
224
203
  })
225
204
 
226
205
  test('pass down MessagePorts', async function (t) {
227
- t.plan(3)
228
-
229
206
  const { port1, port2 } = new MessageChannel()
230
207
  const stream = new ThreadStream({
231
208
  filename: join(__dirname, 'port.js'),
@@ -235,21 +212,19 @@ test('pass down MessagePorts', async function (t) {
235
212
  },
236
213
  sync: false
237
214
  })
238
- t.teardown(() => {
215
+ t.after(() => {
239
216
  stream.end()
240
217
  })
241
218
 
242
- t.ok(stream.write('hello world\n'))
243
- t.ok(stream.write('something else\n'))
219
+ assert.ok(stream.write('hello world\n'))
220
+ assert.ok(stream.write('something else\n'))
244
221
 
245
222
  const [strings] = await once(port2, 'message')
246
223
 
247
- t.equal(strings, 'hello world\nsomething else\n')
224
+ assert.strictEqual(strings, 'hello world\nsomething else\n')
248
225
  })
249
226
 
250
- test('destroy does not error', function (t) {
251
- t.plan(5)
252
-
227
+ test('destroy does not error', function (t, done) {
253
228
  const dest = file()
254
229
  const stream = new ThreadStream({
255
230
  filename: join(__dirname, 'to-file.js'),
@@ -258,28 +233,27 @@ test('destroy does not error', function (t) {
258
233
  })
259
234
 
260
235
  stream.on('ready', () => {
261
- t.pass('ready emitted')
262
236
  stream.worker.terminate()
263
237
  })
264
238
 
265
239
  stream.on('error', (err) => {
266
- t.equal(err.message, 'the worker thread exited')
240
+ assert.strictEqual(err.message, 'the worker thread exited')
267
241
  stream.flush((err) => {
268
- t.equal(err.message, 'the worker has exited')
242
+ assert.strictEqual(err.message, 'the worker has exited')
269
243
  })
270
- t.doesNotThrow(() => stream.flushSync())
271
- t.doesNotThrow(() => stream.end())
244
+ assert.doesNotThrow(() => stream.flushSync())
245
+ assert.doesNotThrow(() => stream.end())
246
+ done()
272
247
  })
273
248
  })
274
249
 
275
- test('syntax error', function (t) {
276
- t.plan(1)
277
-
250
+ test('syntax error', function (t, done) {
278
251
  const stream = new ThreadStream({
279
252
  filename: join(__dirname, 'syntax-error.mjs')
280
253
  })
281
254
 
282
255
  stream.on('error', (err) => {
283
- t.equal(err.message, 'Unexpected end of input')
256
+ assert.strictEqual(err.message, 'Unexpected end of input')
257
+ done()
284
258
  })
285
259
  })