thread-stream 3.0.2 → 3.2.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.
@@ -0,0 +1,15 @@
1
+ {
2
+ "permissions": {
3
+ "allow": [
4
+ "Bash(npx tap:*)",
5
+ "Bash(node:*)",
6
+ "Bash(for i in 1 2 3 4 5)",
7
+ "Bash(do)",
8
+ "Bash(echo:*)",
9
+ "Bash(done)",
10
+ "Bash(npm test:*)"
11
+ ],
12
+ "deny": [],
13
+ "ask": []
14
+ }
15
+ }
@@ -62,7 +62,7 @@ jobs:
62
62
  run: npm run test:ci
63
63
 
64
64
  - name: Coveralls Parallel
65
- uses: coverallsapp/github-action@v2.2.3
65
+ uses: coverallsapp/github-action@v2.3.0
66
66
  with:
67
67
  github-token: ${{ secrets.github_token }}
68
68
  parallel: true
@@ -73,7 +73,7 @@ jobs:
73
73
  runs-on: ubuntu-latest
74
74
  steps:
75
75
  - name: Coveralls Finished
76
- uses: coverallsapp/github-action@v2.2.3
76
+ uses: coverallsapp/github-action@v2.3.0
77
77
  with:
78
78
  github-token: ${{ secrets.GITHUB_TOKEN }}
79
79
  parallel-finished: true
package/CLAUDE.md ADDED
@@ -0,0 +1,64 @@
1
+ # CLAUDE.md
2
+
3
+ This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4
+
5
+ ## Project Overview
6
+
7
+ thread-stream is a library for streaming data to a Node.js Worker Thread. It uses SharedArrayBuffer and Atomics for efficient inter-thread communication, enabling high-performance data streaming to worker threads.
8
+
9
+ ## Build & Test Commands
10
+
11
+ ```bash
12
+ npm test # Run linting (standard), type checking, and all tests
13
+ npm run build # Type check only (tsc --noEmit)
14
+ npm run test:ci # CI-specific test run
15
+
16
+ # Run a single test file
17
+ node --test test/<filename>.test.js
18
+ node --test test/<filename>.test.ts # For TypeScript tests
19
+
20
+ # Lint
21
+ npx standard
22
+ ```
23
+
24
+ ## Architecture
25
+
26
+ ### Core Components
27
+
28
+ - **index.js**: Main `ThreadStream` class extending EventEmitter. Manages shared memory buffers, worker lifecycle, and provides stream-like write/flush/end API.
29
+
30
+ - **lib/worker.js**: Runs inside the Worker Thread. Loads the user-provided destination module, reads from shared buffer, and writes to the destination stream.
31
+
32
+ - **lib/indexes.js**: Defines shared buffer index constants (`WRITE_INDEX`, `READ_INDEX`) used for Atomics-based synchronization.
33
+
34
+ - **lib/wait.js**: Provides `wait()` and `waitDiff()` utilities for async waiting on Atomics state changes with exponential backoff.
35
+
36
+ ### Shared Memory Communication
37
+
38
+ The main thread and worker communicate via two SharedArrayBuffers:
39
+ 1. **stateBuf**: Int32Array for READ_INDEX and WRITE_INDEX positions
40
+ 2. **dataBuf**: Buffer for actual string data (default 4MB)
41
+
42
+ Write flow: Main thread writes to dataBuf, updates WRITE_INDEX, worker reads data between READ_INDEX and WRITE_INDEX, updates READ_INDEX when consumed.
43
+
44
+ ### Worker Module Interface
45
+
46
+ User-provided worker modules must export an async function that receives `workerData` and returns a writable stream:
47
+
48
+ ```js
49
+ async function run(opts) {
50
+ const stream = fs.createWriteStream(opts.dest)
51
+ await once(stream, 'open')
52
+ return stream
53
+ }
54
+ module.exports = run
55
+ ```
56
+
57
+ ### Sync vs Async Modes
58
+
59
+ - `sync: true`: Blocking writes using flushSync, waits for worker to consume
60
+ - `sync: false` (default): Non-blocking writes with drain events when buffer fills
61
+
62
+ ## Code Style
63
+
64
+ Uses [Standard](https://standardjs.com/) for linting. Test files in `test/ts/**/*` and `test/syntax-error.mjs` are excluded from linting.
package/README.md CHANGED
@@ -87,7 +87,8 @@ This module works with `yarn` in PnP (plug'n play) mode too!
87
87
  ### Emit events
88
88
 
89
89
  You can emit events on the ThreadStream from your worker using [`worker.parentPort.postMessage()`](https://nodejs.org/api/worker_threads.html#workerparentport).
90
- The message (JSON object) must have the following data structure:
90
+ Messages that do not carry a thread-stream protocol `code` are ignored.
91
+ For custom events, the message (JSON object) must have the following data structure:
91
92
 
92
93
  ```js
93
94
  parentPort.postMessage({
package/index.js CHANGED
@@ -8,7 +8,8 @@ const { pathToFileURL } = require('url')
8
8
  const { wait } = require('./lib/wait')
9
9
  const {
10
10
  WRITE_INDEX,
11
- READ_INDEX
11
+ READ_INDEX,
12
+ SEQ_INDEX
12
13
  } = require('./lib/indexes')
13
14
  const buffer = require('buffer')
14
15
  const assert = require('assert')
@@ -18,6 +19,13 @@ const kImpl = Symbol('kImpl')
18
19
  // V8 limit for string size
19
20
  const MAX_STRING = buffer.constants.MAX_STRING_LENGTH
20
21
 
22
+ function updateState (stream, fn) {
23
+ Atomics.add(stream[kImpl].state, SEQ_INDEX, 1)
24
+ fn()
25
+ Atomics.add(stream[kImpl].state, SEQ_INDEX, 1)
26
+ Atomics.notify(stream[kImpl].state, SEQ_INDEX)
27
+ }
28
+
21
29
  class FakeWeakRef {
22
30
  constructor (value) {
23
31
  this._value = value
@@ -120,8 +128,11 @@ function nextFlush (stream) {
120
128
  return
121
129
  }
122
130
 
123
- Atomics.store(stream[kImpl].state, READ_INDEX, 0)
124
- Atomics.store(stream[kImpl].state, WRITE_INDEX, 0)
131
+ updateState(stream, () => {
132
+ Atomics.store(stream[kImpl].state, READ_INDEX, 0)
133
+ Atomics.store(stream[kImpl].state, WRITE_INDEX, 0)
134
+ })
135
+ Atomics.notify(stream[kImpl].state, READ_INDEX)
125
136
 
126
137
  // Find a toWrite length that fits the buffer
127
138
  // it must exists as the buffer is at least 4 bytes length
@@ -141,8 +152,11 @@ function nextFlush (stream) {
141
152
  return
142
153
  }
143
154
  stream.flush(() => {
144
- Atomics.store(stream[kImpl].state, READ_INDEX, 0)
145
- Atomics.store(stream[kImpl].state, WRITE_INDEX, 0)
155
+ updateState(stream, () => {
156
+ Atomics.store(stream[kImpl].state, READ_INDEX, 0)
157
+ Atomics.store(stream[kImpl].state, WRITE_INDEX, 0)
158
+ })
159
+ Atomics.notify(stream[kImpl].state, READ_INDEX)
146
160
  nextFlush(stream)
147
161
  })
148
162
  } else {
@@ -160,6 +174,12 @@ function onWorkerMessage (msg) {
160
174
  return
161
175
  }
162
176
 
177
+ // Node.js watch mode may send internal worker messages that do not
178
+ // participate in thread-stream's worker protocol.
179
+ if (msg?.code == null) {
180
+ return
181
+ }
182
+
163
183
  switch (msg.code) {
164
184
  case 'READY':
165
185
  // Replace the FakeWeakRef with a
@@ -401,8 +421,9 @@ function write (stream, data, cb) {
401
421
  const current = Atomics.load(stream[kImpl].state, WRITE_INDEX)
402
422
  const length = Buffer.byteLength(data)
403
423
  stream[kImpl].data.write(data, current)
404
- Atomics.store(stream[kImpl].state, WRITE_INDEX, current + length)
405
- Atomics.notify(stream[kImpl].state, WRITE_INDEX)
424
+ updateState(stream, () => {
425
+ Atomics.store(stream[kImpl].state, WRITE_INDEX, current + length)
426
+ })
406
427
  cb()
407
428
  return true
408
429
  }
@@ -419,9 +440,10 @@ function end (stream) {
419
440
  let readIndex = Atomics.load(stream[kImpl].state, READ_INDEX)
420
441
 
421
442
  // process._rawDebug('writing index')
422
- Atomics.store(stream[kImpl].state, WRITE_INDEX, -1)
443
+ updateState(stream, () => {
444
+ Atomics.store(stream[kImpl].state, WRITE_INDEX, -1)
445
+ })
423
446
  // process._rawDebug(`(end) readIndex (${Atomics.load(stream.state, READ_INDEX)}) writeIndex (${Atomics.load(stream.state, WRITE_INDEX)})`)
424
- Atomics.notify(stream[kImpl].state, WRITE_INDEX)
425
447
 
426
448
  // Wait for the process to complete
427
449
  let spins = 0
@@ -466,8 +488,11 @@ function writeSync (stream) {
466
488
  let leftover = stream[kImpl].data.length - writeIndex
467
489
  if (leftover === 0) {
468
490
  flushSync(stream)
469
- Atomics.store(stream[kImpl].state, READ_INDEX, 0)
470
- Atomics.store(stream[kImpl].state, WRITE_INDEX, 0)
491
+ updateState(stream, () => {
492
+ Atomics.store(stream[kImpl].state, READ_INDEX, 0)
493
+ Atomics.store(stream[kImpl].state, WRITE_INDEX, 0)
494
+ })
495
+ Atomics.notify(stream[kImpl].state, READ_INDEX)
471
496
  continue
472
497
  } else if (leftover < 0) {
473
498
  // stream should never happen
@@ -483,8 +508,11 @@ function writeSync (stream) {
483
508
  } else {
484
509
  // multi-byte utf-8
485
510
  flushSync(stream)
486
- Atomics.store(stream[kImpl].state, READ_INDEX, 0)
487
- Atomics.store(stream[kImpl].state, WRITE_INDEX, 0)
511
+ updateState(stream, () => {
512
+ Atomics.store(stream[kImpl].state, READ_INDEX, 0)
513
+ Atomics.store(stream[kImpl].state, WRITE_INDEX, 0)
514
+ })
515
+ Atomics.notify(stream[kImpl].state, READ_INDEX)
488
516
 
489
517
  // Find a toWrite length that fits the buffer
490
518
  // it must exists as the buffer is at least 4 bytes length
package/lib/indexes.js CHANGED
@@ -1,9 +1,11 @@
1
1
  'use strict'
2
2
 
3
+ const SEQ_INDEX = 2
3
4
  const WRITE_INDEX = 4
4
5
  const READ_INDEX = 8
5
6
 
6
7
  module.exports = {
7
8
  WRITE_INDEX,
8
- READ_INDEX
9
+ READ_INDEX,
10
+ SEQ_INDEX
9
11
  }
package/lib/worker.js CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  const { realImport, realRequire } = require('real-require')
4
4
  const { workerData, parentPort } = require('worker_threads')
5
- const { WRITE_INDEX, READ_INDEX } = require('./indexes')
5
+ const { WRITE_INDEX, READ_INDEX, SEQ_INDEX } = require('./indexes')
6
6
  const { waitDiff } = require('./wait')
7
7
 
8
8
  const {
@@ -48,7 +48,11 @@ async function start () {
48
48
  // When bundled with pkg, an undefined error is thrown when called with realImport
49
49
  // When bundled with pkg and using node v20, an ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING error is thrown when called with realImport
50
50
  // More info at: https://github.com/pinojs/thread-stream/issues/143
51
- worker = realRequire(decodeURIComponent(filename.replace(process.platform === 'win32' ? 'file:///' : 'file://', '')))
51
+ try {
52
+ worker = realRequire(decodeURIComponent(filename.replace(process.platform === 'win32' ? 'file:///' : 'file://', '')))
53
+ } catch {
54
+ throw error
55
+ }
52
56
  } else {
53
57
  throw error
54
58
  }
@@ -97,18 +101,30 @@ start().then(function () {
97
101
  process.nextTick(run)
98
102
  })
99
103
 
104
+ function readState () {
105
+ while (true) {
106
+ const seq = Atomics.load(state, SEQ_INDEX)
107
+
108
+ if ((seq & 1) !== 0) {
109
+ continue
110
+ }
111
+
112
+ const current = Atomics.load(state, READ_INDEX)
113
+ const end = Atomics.load(state, WRITE_INDEX)
114
+
115
+ if (seq === Atomics.load(state, SEQ_INDEX)) {
116
+ return { current, end, seq }
117
+ }
118
+ }
119
+ }
120
+
100
121
  function run () {
101
- const current = Atomics.load(state, READ_INDEX)
102
- const end = Atomics.load(state, WRITE_INDEX)
122
+ const { current, end, seq } = readState()
103
123
 
104
124
  // process._rawDebug(`pre state ${current} ${end}`)
105
125
 
106
126
  if (end === current) {
107
- if (end === data.length) {
108
- waitDiff(state, READ_INDEX, end, Infinity, run)
109
- } else {
110
- waitDiff(state, WRITE_INDEX, end, Infinity, run)
111
- }
127
+ waitDiff(state, SEQ_INDEX, seq, Infinity, run)
112
128
  return
113
129
  }
114
130
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "thread-stream",
3
- "version": "3.0.2",
3
+ "version": "3.2.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",
@@ -34,7 +34,8 @@
34
34
  },
35
35
  "standard": {
36
36
  "ignore": [
37
- "test/ts/**/*"
37
+ "test/ts/**/*",
38
+ "test/syntax-error.mjs"
38
39
  ]
39
40
  },
40
41
  "repository": {
package/test/base.test.js CHANGED
@@ -8,9 +8,19 @@ const ThreadStream = require('..')
8
8
  const { MessageChannel } = require('worker_threads')
9
9
  const { once } = require('events')
10
10
 
11
- test('base sync=true', function (t) {
12
- t.plan(15)
11
+ function readFileAsync (path) {
12
+ return new Promise((resolve, reject) => {
13
+ readFile(path, 'utf8', (err, data) => {
14
+ if (err) {
15
+ reject(err)
16
+ return
17
+ }
18
+ resolve(data)
19
+ })
20
+ })
21
+ }
13
22
 
23
+ test('base sync=true', async function (t) {
14
24
  const dest = file()
15
25
  const stream = new ThreadStream({
16
26
  filename: join(__dirname, 'to-file.js'),
@@ -18,24 +28,12 @@ test('base sync=true', function (t) {
18
28
  sync: true
19
29
  })
20
30
 
21
- t.same(stream.writableObjectMode, false)
31
+ const finish = once(stream, 'finish')
32
+ const close = once(stream, 'close')
22
33
 
34
+ t.same(stream.writableObjectMode, false)
23
35
  t.same(stream.writableFinished, false)
24
- stream.on('finish', () => {
25
- t.same(stream.writableFinished, true)
26
- readFile(dest, 'utf8', (err, data) => {
27
- t.error(err)
28
- t.equal(data, 'hello world\nsomething else\n')
29
- })
30
- })
31
-
32
36
  t.same(stream.closed, false)
33
- stream.on('close', () => {
34
- t.same(stream.closed, true)
35
- t.notOk(stream.writable)
36
- t.pass('close emitted')
37
- })
38
-
39
37
  t.same(stream.writableNeedDrain, false)
40
38
  t.ok(stream.write('hello world\n'))
41
39
  t.ok(stream.write('something else\n'))
@@ -44,11 +42,19 @@ test('base sync=true', function (t) {
44
42
  t.same(stream.writableEnded, false)
45
43
  stream.end()
46
44
  t.same(stream.writableEnded, true)
47
- })
48
45
 
49
- test('overflow sync=true', function (t) {
50
- t.plan(3)
46
+ await finish
47
+ t.same(stream.writableFinished, true)
48
+
49
+ await close
50
+ t.same(stream.closed, true)
51
+ t.notOk(stream.writable)
52
+
53
+ const data = await readFileAsync(dest)
54
+ t.equal(data, 'hello world\nsomething else\n')
55
+ })
51
56
 
57
+ test('overflow sync=true', async function (t) {
52
58
  const dest = file()
53
59
  const stream = new ThreadStream({
54
60
  bufferSize: 128,
@@ -57,9 +63,9 @@ test('overflow sync=true', function (t) {
57
63
  sync: true
58
64
  })
59
65
 
66
+ const close = once(stream, 'close')
60
67
  let count = 0
61
68
 
62
- // Write 10 chars, 20 times
63
69
  function write () {
64
70
  if (count++ === 20) {
65
71
  stream.end()
@@ -67,25 +73,17 @@ test('overflow sync=true', function (t) {
67
73
  }
68
74
 
69
75
  stream.write('aaaaaaaaaa')
70
- // do not wait for drain event
71
76
  setImmediate(write)
72
77
  }
73
78
 
74
79
  write()
75
80
 
76
- stream.on('finish', () => {
77
- t.pass('finish emitted')
78
- })
79
-
80
- stream.on('close', () => {
81
- readFile(dest, 'utf8', (err, data) => {
82
- t.error(err)
83
- t.equal(data.length, 200)
84
- })
85
- })
81
+ await close
82
+ const data = await readFileAsync(dest)
83
+ t.equal(data.length, 200)
86
84
  })
87
85
 
88
- test('overflow sync=false', function (t) {
86
+ test('overflow sync=false', async function (t) {
89
87
  const dest = file()
90
88
  const stream = new ThreadStream({
91
89
  bufferSize: 128,
@@ -94,14 +92,13 @@ test('overflow sync=false', function (t) {
94
92
  sync: false
95
93
  })
96
94
 
95
+ const close = once(stream, 'close')
97
96
  let count = 0
98
97
 
99
98
  t.same(stream.writableNeedDrain, false)
100
99
 
101
- // Write 10 chars, 20 times
102
100
  function write () {
103
101
  if (count++ === 20) {
104
- t.pass('end sent')
105
102
  stream.end()
106
103
  return
107
104
  }
@@ -109,7 +106,6 @@ test('overflow sync=false', function (t) {
109
106
  if (!stream.write('aaaaaaaaaa')) {
110
107
  t.same(stream.writableNeedDrain, true)
111
108
  }
112
- // do not wait for drain event
113
109
  setImmediate(write)
114
110
  }
115
111
 
@@ -117,24 +113,15 @@ test('overflow sync=false', function (t) {
117
113
 
118
114
  stream.on('drain', () => {
119
115
  t.same(stream.writableNeedDrain, false)
120
- t.pass('drain')
121
- })
122
-
123
- stream.on('finish', () => {
124
- t.pass('finish emitted')
125
116
  })
126
117
 
127
- stream.on('close', () => {
128
- readFile(dest, 'utf8', (err, data) => {
129
- t.error(err)
130
- t.equal(data.length, 200)
131
- t.end()
132
- })
133
- })
118
+ await close
119
+ const data = await readFileAsync(dest)
120
+ t.equal(data.length, 200)
134
121
  })
135
122
 
136
123
  test('over the bufferSize at startup', function (t) {
137
- t.plan(6)
124
+ t.plan(5)
138
125
 
139
126
  const dest = file()
140
127
  const stream = new ThreadStream({
@@ -151,10 +138,6 @@ test('over the bufferSize at startup', function (t) {
151
138
  })
152
139
  })
153
140
 
154
- stream.on('close', () => {
155
- t.pass('close emitted')
156
- })
157
-
158
141
  t.ok(stream.write('hello'))
159
142
  t.ok(stream.write(' world\n'))
160
143
  t.ok(stream.write('something else\n'))
@@ -163,7 +146,7 @@ test('over the bufferSize at startup', function (t) {
163
146
  })
164
147
 
165
148
  test('over the bufferSize at startup (async)', function (t) {
166
- t.plan(6)
149
+ t.plan(5)
167
150
 
168
151
  const dest = file()
169
152
  const stream = new ThreadStream({
@@ -185,13 +168,11 @@ test('over the bufferSize at startup (async)', function (t) {
185
168
  t.equal(data, 'hello world\nsomething else\n')
186
169
  })
187
170
  })
188
-
189
- stream.on('close', () => {
190
- t.pass('close emitted')
191
- })
192
171
  })
193
172
 
194
173
  test('flushSync sync=false', function (t) {
174
+ t.plan(2)
175
+
195
176
  const dest = file()
196
177
  const stream = new ThreadStream({
197
178
  bufferSize: 128,
@@ -200,32 +181,26 @@ test('flushSync sync=false', function (t) {
200
181
  sync: false
201
182
  })
202
183
 
203
- stream.on('drain', () => {
204
- t.pass('drain')
205
- stream.end()
206
- })
184
+ stream.on('ready', () => {
185
+ for (let count = 0; count < 20; count++) {
186
+ stream.write('aaaaaaaaaa')
187
+ }
207
188
 
208
- stream.on('finish', () => {
209
- t.pass('finish emitted')
189
+ stream.flushSync()
190
+ setImmediate(() => {
191
+ stream.end()
192
+ })
210
193
  })
211
194
 
212
- stream.on('close', () => {
195
+ stream.on('finish', () => {
213
196
  readFile(dest, 'utf8', (err, data) => {
214
197
  t.error(err)
215
198
  t.equal(data.length, 200)
216
- t.end()
217
199
  })
218
200
  })
219
-
220
- for (let count = 0; count < 20; count++) {
221
- stream.write('aaaaaaaaaa')
222
- }
223
- stream.flushSync()
224
201
  })
225
202
 
226
203
  test('pass down MessagePorts', async function (t) {
227
- t.plan(3)
228
-
229
204
  const { port1, port2 } = new MessageChannel()
230
205
  const stream = new ThreadStream({
231
206
  filename: join(__dirname, 'port.js'),
@@ -235,6 +210,7 @@ test('pass down MessagePorts', async function (t) {
235
210
  },
236
211
  sync: false
237
212
  })
213
+
238
214
  t.teardown(() => {
239
215
  stream.end()
240
216
  })
@@ -243,13 +219,10 @@ test('pass down MessagePorts', async function (t) {
243
219
  t.ok(stream.write('something else\n'))
244
220
 
245
221
  const [strings] = await once(port2, 'message')
246
-
247
222
  t.equal(strings, 'hello world\nsomething else\n')
248
223
  })
249
224
 
250
- test('destroy does not error', function (t) {
251
- t.plan(5)
252
-
225
+ test('destroy does not error', async function (t) {
253
226
  const dest = file()
254
227
  const stream = new ThreadStream({
255
228
  filename: join(__dirname, 'to-file.js'),
@@ -258,16 +231,28 @@ test('destroy does not error', function (t) {
258
231
  })
259
232
 
260
233
  stream.on('ready', () => {
261
- t.pass('ready emitted')
262
234
  stream.worker.terminate()
263
235
  })
264
236
 
265
- stream.on('error', (err) => {
266
- t.equal(err.message, 'the worker thread exited')
237
+ const [err] = await once(stream, 'error')
238
+ t.equal(err.message, 'the worker thread exited')
239
+
240
+ await new Promise((resolve) => {
267
241
  stream.flush((err) => {
268
242
  t.equal(err.message, 'the worker has exited')
243
+ resolve()
269
244
  })
270
- t.doesNotThrow(() => stream.flushSync())
271
- t.doesNotThrow(() => stream.end())
272
245
  })
246
+
247
+ t.doesNotThrow(() => stream.flushSync())
248
+ t.doesNotThrow(() => stream.end())
249
+ })
250
+
251
+ test('syntax error', async function (t) {
252
+ const stream = new ThreadStream({
253
+ filename: join(__dirname, 'syntax-error.mjs')
254
+ })
255
+
256
+ const [err] = await once(stream, 'error')
257
+ t.equal(err.message, 'Unexpected end of input')
273
258
  })
@@ -0,0 +1,19 @@
1
+ 'use strict'
2
+
3
+ const { Writable } = require('stream')
4
+ const { parentPort } = require('worker_threads')
5
+
6
+ async function run () {
7
+ parentPort.postMessage({
8
+ internal: 'watch-mode'
9
+ })
10
+
11
+ return new Writable({
12
+ autoDestroy: true,
13
+ write (chunk, enc, cb) {
14
+ cb()
15
+ }
16
+ })
17
+ }
18
+
19
+ module.exports = run
@@ -0,0 +1,2 @@
1
+ // this is a syntax error
2
+ import
@@ -0,0 +1,28 @@
1
+ 'use strict'
2
+
3
+ const { test } = require('tap')
4
+ const { join } = require('path')
5
+ const ThreadStream = require('..')
6
+
7
+ test('ignores worker messages without a protocol code', function (t) {
8
+ t.plan(2)
9
+
10
+ const stream = new ThreadStream({
11
+ filename: join(__dirname, 'message-without-code.js'),
12
+ sync: false
13
+ })
14
+
15
+ const errors = []
16
+ stream.on('error', err => {
17
+ errors.push(err)
18
+ })
19
+
20
+ stream.on('ready', () => {
21
+ t.ok(stream.write('hello world\n'))
22
+ stream.end()
23
+ })
24
+
25
+ stream.on('finish', () => {
26
+ t.same(errors, [])
27
+ })
28
+ })