thread-stream 3.1.0 → 4.1.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,68 @@
1
+ 'use strict'
2
+
3
+ const { EventEmitter } = require('events')
4
+ const { parentPort } = require('worker_threads')
5
+
6
+ function createDestination (mode) {
7
+ const destination = new EventEmitter()
8
+ destination.writableEnded = false
9
+ destination.writableNeedDrain = false
10
+
11
+ destination.write = function () {
12
+ if (mode === 'drain') {
13
+ destination.writableNeedDrain = true
14
+ setTimeout(() => {
15
+ destination.writableNeedDrain = false
16
+ parentPort.postMessage({
17
+ code: 'EVENT',
18
+ name: 'destination-drain'
19
+ })
20
+ destination.emit('drain')
21
+ }, 50)
22
+ }
23
+
24
+ return true
25
+ }
26
+
27
+ destination.end = function () {
28
+ destination.writableEnded = true
29
+ destination.emit('close')
30
+ }
31
+
32
+ if (mode === 'flush') {
33
+ destination.flush = function (cb) {
34
+ setTimeout(() => {
35
+ parentPort.postMessage({
36
+ code: 'EVENT',
37
+ name: 'destination-flushed'
38
+ })
39
+ cb()
40
+ }, 50)
41
+ }
42
+ }
43
+
44
+ if (mode === 'flush-sync') {
45
+ destination.flushSync = function () {
46
+ parentPort.postMessage({
47
+ code: 'EVENT',
48
+ name: 'destination-flush-sync'
49
+ })
50
+ }
51
+ }
52
+
53
+ if (mode === 'exit-on-flush') {
54
+ destination.flush = function (_cb) {
55
+ setTimeout(() => {
56
+ process.exit(0)
57
+ }, 20)
58
+ }
59
+ }
60
+
61
+ return destination
62
+ }
63
+
64
+ async function run (opts) {
65
+ return createDestination(opts.mode)
66
+ }
67
+
68
+ module.exports = run
@@ -0,0 +1,112 @@
1
+ 'use strict'
2
+
3
+ const { test } = require('node:test')
4
+ const assert = require('node:assert')
5
+ const { once } = require('node:events')
6
+ const { join } = require('node:path')
7
+ const ThreadStream = require('..')
8
+
9
+ function createStream (mode) {
10
+ return new ThreadStream({
11
+ filename: join(__dirname, 'flush-worker.js'),
12
+ workerData: { mode },
13
+ sync: false
14
+ })
15
+ }
16
+
17
+ test('flush waits for worker destination.flush(cb)', async function () {
18
+ const stream = createStream('flush')
19
+ let flushed = false
20
+
21
+ stream.on('destination-flushed', () => {
22
+ flushed = true
23
+ })
24
+
25
+ assert.ok(stream.write('hello'))
26
+
27
+ await new Promise((resolve, reject) => {
28
+ stream.flush((err) => {
29
+ if (err) {
30
+ reject(err)
31
+ return
32
+ }
33
+ resolve()
34
+ })
35
+ })
36
+
37
+ assert.strictEqual(flushed, true)
38
+
39
+ const close = once(stream, 'close')
40
+ stream.end()
41
+ await close
42
+ })
43
+
44
+ test('flush falls back to destination.flushSync()', async function () {
45
+ const stream = createStream('flush-sync')
46
+ let called = false
47
+
48
+ stream.on('destination-flush-sync', () => {
49
+ called = true
50
+ })
51
+
52
+ assert.ok(stream.write('hello'))
53
+
54
+ await new Promise((resolve, reject) => {
55
+ stream.flush((err) => {
56
+ if (err) {
57
+ reject(err)
58
+ return
59
+ }
60
+ resolve()
61
+ })
62
+ })
63
+
64
+ assert.strictEqual(called, true)
65
+
66
+ const close = once(stream, 'close')
67
+ stream.end()
68
+ await close
69
+ })
70
+
71
+ test('flush waits for drain when destination has no flush API', async function () {
72
+ const stream = createStream('drain')
73
+ let drained = false
74
+
75
+ stream.on('destination-drain', () => {
76
+ drained = true
77
+ })
78
+
79
+ assert.ok(stream.write('hello'))
80
+
81
+ await new Promise((resolve, reject) => {
82
+ stream.flush((err) => {
83
+ if (err) {
84
+ reject(err)
85
+ return
86
+ }
87
+ resolve()
88
+ })
89
+ })
90
+
91
+ assert.strictEqual(drained, true)
92
+
93
+ const close = once(stream, 'close')
94
+ stream.end()
95
+ await close
96
+ })
97
+
98
+ test('pending flush callbacks fail when worker exits', async function () {
99
+ const stream = createStream('exit-on-flush')
100
+ const close = once(stream, 'close')
101
+
102
+ assert.ok(stream.write('hello'))
103
+
104
+ const err = await new Promise((resolve) => {
105
+ stream.flush(resolve)
106
+ })
107
+
108
+ assert.ok(err)
109
+ assert.strictEqual(err.message, 'the worker has exited')
110
+
111
+ await close
112
+ })
package/test/helper.js CHANGED
@@ -3,7 +3,6 @@
3
3
  const { join } = require('path')
4
4
  const { tmpdir } = require('os')
5
5
  const { unlinkSync } = require('fs')
6
- const t = require('tap')
7
6
 
8
7
  const files = []
9
8
  let count = 0
@@ -15,21 +14,13 @@ function file () {
15
14
  }
16
15
 
17
16
  process.on('beforeExit', () => {
18
- t.comment('unlink files')
19
17
  for (const file of files) {
20
18
  try {
21
- t.comment(`unliking ${file}`)
22
19
  unlinkSync(file)
23
20
  } catch (e) {
24
- console.log(e)
21
+ // ignore cleanup errors
25
22
  }
26
23
  }
27
- t.comment('unlink completed')
28
24
  })
29
25
 
30
26
  module.exports.file = file
31
-
32
- if (process.env.SKIP_PROCESS_EXIT_CHECK !== 'true') {
33
- const why = require('why-is-node-running')
34
- setInterval(why, 10000).unref()
35
- }
@@ -1,11 +1,11 @@
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 indexes = require('../lib/indexes')
5
6
 
6
7
  for (const index of Object.keys(indexes)) {
7
- test(`${index} is lock free`, function (t) {
8
- t.equal(Atomics.isLockFree(indexes[index]), true)
9
- t.end()
8
+ test(`${index} is lock free`, function () {
9
+ assert.strictEqual(Atomics.isLockFree(indexes[index]), true)
10
10
  })
11
11
  }
@@ -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
@@ -1,11 +1,11 @@
1
- import { test } from 'tap'
1
+ import { test } from 'node:test'
2
+ import assert from 'node:assert'
2
3
  import { readFile } from 'fs'
3
4
  import ThreadStream from '../index.js'
4
5
  import { join } from 'desm'
5
6
  import { file } from './helper.js'
6
7
 
7
- test('break up utf8 multibyte (sync)', (t) => {
8
- t.plan(2)
8
+ test('break up utf8 multibyte (sync)', (t, done) => {
9
9
  const longString = '\u03A3'.repeat(16)
10
10
 
11
11
  const dest = file()
@@ -18,8 +18,9 @@ test('break up utf8 multibyte (sync)', (t) => {
18
18
 
19
19
  stream.on('finish', () => {
20
20
  readFile(dest, 'utf8', (err, data) => {
21
- t.error(err)
22
- t.equal(data, longString)
21
+ assert.ifError(err)
22
+ assert.strictEqual(data, longString)
23
+ done()
23
24
  })
24
25
  })
25
26
 
@@ -27,8 +28,7 @@ test('break up utf8 multibyte (sync)', (t) => {
27
28
  stream.end()
28
29
  })
29
30
 
30
- test('break up utf8 multibyte (async)', (t) => {
31
- t.plan(2)
31
+ test('break up utf8 multibyte (async)', (t, done) => {
32
32
  const longString = '\u03A3'.repeat(16)
33
33
 
34
34
  const dest = file()
@@ -41,8 +41,9 @@ test('break up utf8 multibyte (async)', (t) => {
41
41
 
42
42
  stream.on('finish', () => {
43
43
  readFile(dest, 'utf8', (err, data) => {
44
- t.error(err)
45
- t.equal(data, longString)
44
+ assert.ifError(err)
45
+ assert.strictEqual(data, longString)
46
+ done()
46
47
  })
47
48
  })
48
49
 
@@ -50,8 +51,7 @@ test('break up utf8 multibyte (async)', (t) => {
50
51
  stream.end()
51
52
  })
52
53
 
53
- test('break up utf8 multibyte several times bigger than write buffer', (t) => {
54
- t.plan(2)
54
+ test('break up utf8 multibyte several times bigger than write buffer', (t, done) => {
55
55
  const longString = '\u03A3'.repeat(32)
56
56
 
57
57
  const dest = file()
@@ -64,8 +64,9 @@ test('break up utf8 multibyte several times bigger than write buffer', (t) => {
64
64
 
65
65
  stream.on('finish', () => {
66
66
  readFile(dest, 'utf8', (err, data) => {
67
- t.error(err)
68
- t.equal(data, longString)
67
+ assert.ifError(err)
68
+ assert.strictEqual(data, longString)
69
+ done()
69
70
  })
70
71
  })
71
72
 
package/test/pkg/index.js CHANGED
@@ -1,37 +1,37 @@
1
1
  'use strict'
2
2
 
3
3
  /**
4
- * This file is packaged using pkg in order to test if worker.js works in that context
4
+ * This file is packaged using pkg in order to test if worker.js works in that context.
5
+ * Note: We can't use node:test here because it crashes inside pkg bundles due to V8 internals.
5
6
  */
6
7
 
7
- const { test } = require('tap')
8
+ const assert = require('node:assert')
8
9
  const { join } = require('path')
9
10
  const { file } = require('../helper')
10
11
  const ThreadStream = require('../..')
11
12
 
12
- test('bundlers support with .js file', function (t) {
13
- t.plan(1)
13
+ globalThis.__bundlerPathsOverrides = {
14
+ 'thread-stream-worker': join(__dirname, '..', 'custom-worker.js')
15
+ }
14
16
 
15
- globalThis.__bundlerPathsOverrides = {
16
- 'thread-stream-worker': join(__dirname, '..', 'custom-worker.js')
17
- }
17
+ const dest = file()
18
18
 
19
- const dest = file()
20
-
21
- process.on('uncaughtException', (error) => {
22
- console.log(error)
23
- })
24
-
25
- const stream = new ThreadStream({
26
- filename: join(__dirname, '..', 'to-file.js'),
27
- workerData: { dest },
28
- sync: true
29
- })
19
+ process.on('uncaughtException', (error) => {
20
+ console.error(error)
21
+ process.exit(1)
22
+ })
30
23
 
31
- stream.worker.removeAllListeners('message')
32
- stream.worker.once('message', (message) => {
33
- t.equal(message.code, 'CUSTOM-WORKER-CALLED')
34
- })
24
+ const stream = new ThreadStream({
25
+ filename: join(__dirname, '..', 'to-file.js'),
26
+ workerData: { dest },
27
+ sync: true
28
+ })
35
29
 
36
- stream.end()
30
+ stream.worker.removeAllListeners('message')
31
+ stream.worker.once('message', (message) => {
32
+ assert.strictEqual(message.code, 'CUSTOM-WORKER-CALLED')
33
+ console.log('pkg test passed')
34
+ process.exit(0)
37
35
  })
36
+
37
+ stream.end()
@@ -5,10 +5,9 @@
5
5
  "../to-file.js"
6
6
  ],
7
7
  "targets": [
8
- "node14",
9
- "node16",
10
- "node18",
11
- "node20"
8
+ "node20",
9
+ "node22",
10
+ "node24"
12
11
  ],
13
12
  "outputPath": "test/pkg"
14
13
  }
@@ -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 config = require('./pkg.config.json')
5
6
  const { promisify } = require('util')
6
7
  const { unlink } = require('fs/promises')
@@ -8,7 +9,7 @@ const { join } = require('path')
8
9
  const { platform } = require('process')
9
10
  const exec = promisify(require('child_process').exec)
10
11
 
11
- test('worker test when packaged into executable using pkg', async (t) => {
12
+ test('worker test when packaged into executable using pkg', async () => {
12
13
  const packageName = 'index'
13
14
 
14
15
  // package the app into several node versions, check config for more info
@@ -19,7 +20,7 @@ test('worker test when packaged into executable using pkg', async (t) => {
19
20
  const { stderr } = await exec(`npx pkg ${filePath} --config ${configPath}`)
20
21
 
21
22
  // there should be no error when packaging
22
- t.equal(stderr, '')
23
+ assert.strictEqual(stderr, '')
23
24
 
24
25
  // pkg outputs files in the following format by default: {filename}-{node version}
25
26
  for (const target of config.pkg.targets) {
@@ -36,11 +37,9 @@ test('worker test when packaged into executable using pkg', async (t) => {
36
37
  const { stderr } = await exec(executablePath)
37
38
 
38
39
  // check if there were no errors
39
- t.equal(stderr, '')
40
+ assert.strictEqual(stderr, '')
40
41
 
41
42
  // clean up afterwards
42
43
  await unlink(executablePath)
43
44
  }
44
-
45
- t.end()
46
45
  })
@@ -1,24 +1,23 @@
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 { once } = require('events')
6
7
  const { MessageChannel } = require('worker_threads')
7
8
  const ThreadStream = require('..')
8
9
 
9
10
  test('message events emitted on the stream are posted to the worker', async function (t) {
10
- t.plan(1)
11
-
12
11
  const { port1, port2 } = new MessageChannel()
13
12
  const stream = new ThreadStream({
14
13
  filename: join(__dirname, 'on-message.js'),
15
14
  sync: false
16
15
  })
17
- t.teardown(() => {
16
+ t.after(() => {
18
17
  stream.end()
19
18
  })
20
19
 
21
20
  stream.emit('message', { text: 'hello', takeThisPortPlease: port1 }, [port1])
22
21
  const [confirmation] = await once(port2, 'message')
23
- t.equal(confirmation, 'received: hello')
22
+ assert.strictEqual(confirmation, 'received: hello')
24
23
  })
@@ -0,0 +1,16 @@
1
+ 'use strict'
2
+
3
+ const { Writable } = require('stream')
4
+ const { threadName, parentPort } = require('worker_threads')
5
+
6
+ module.exports = function () {
7
+ parentPort.once('message', function ({ port }) {
8
+ port.postMessage({ threadName })
9
+ })
10
+
11
+ return new Writable({
12
+ write (chunk, encoding, callback) {
13
+ callback()
14
+ }
15
+ })
16
+ }
@@ -1,12 +1,7 @@
1
1
  'use strict'
2
2
 
3
- const t = require('tap')
4
-
5
- if (process.env.CI) {
6
- t.skip('skip on CI')
7
- process.exit(0)
8
- }
9
-
3
+ const { test } = require('node:test')
4
+ const assert = require('node:assert')
10
5
  const { join } = require('path')
11
6
  const { file } = require('./helper')
12
7
  const { createReadStream } = require('fs')
@@ -15,27 +10,26 @@ const buffer = require('buffer')
15
10
 
16
11
  const MAX_STRING = buffer.constants.MAX_STRING_LENGTH
17
12
 
18
- t.plan(1)
19
-
20
- const dest = file()
21
- const stream = new ThreadStream({
22
- filename: join(__dirname, 'to-file.js'),
23
- workerData: { dest },
24
- sync: false
25
- })
26
-
27
- stream.on('close', async () => {
28
- t.comment('close emitted')
29
- let buf
30
- for await (const chunk of createReadStream(dest)) {
31
- buf = chunk
32
- }
33
- t.equal('asd', buf.toString().slice(-3))
34
- })
35
-
36
- stream.on('ready', () => {
37
- t.comment('open emitted')
38
- stream.write('a'.repeat(MAX_STRING - 2))
39
- stream.write('asd')
40
- stream.end()
13
+ test('string limit 2', { skip: process.env.CI }, (t, done) => {
14
+ const dest = file()
15
+ const stream = new ThreadStream({
16
+ filename: join(__dirname, 'to-file.js'),
17
+ workerData: { dest },
18
+ sync: false
19
+ })
20
+
21
+ stream.on('close', async () => {
22
+ let buf
23
+ for await (const chunk of createReadStream(dest)) {
24
+ buf = chunk
25
+ }
26
+ assert.strictEqual('asd', buf.toString().slice(-3))
27
+ done()
28
+ })
29
+
30
+ stream.on('ready', () => {
31
+ stream.write('a'.repeat(MAX_STRING - 2))
32
+ stream.write('asd')
33
+ stream.end()
34
+ })
41
35
  })
@@ -1,42 +1,37 @@
1
1
  'use strict'
2
2
 
3
- const t = require('tap')
4
-
5
- if (process.env.CI) {
6
- t.skip('skip on CI')
7
- process.exit(0)
8
- }
9
-
3
+ const { test } = require('node:test')
4
+ const assert = require('node:assert')
10
5
  const { join } = require('path')
11
6
  const { file } = require('./helper')
12
7
  const { stat } = require('fs')
13
8
  const ThreadStream = require('..')
14
9
 
15
- t.setTimeout(30000)
16
-
17
- const dest = file()
18
- const stream = new ThreadStream({
19
- filename: join(__dirname, 'to-file.js'),
20
- workerData: { dest },
21
- sync: false
22
- })
10
+ test('string limit', { skip: process.env.CI, timeout: 30000 }, (t, done) => {
11
+ const dest = file()
12
+ const stream = new ThreadStream({
13
+ filename: join(__dirname, 'to-file.js'),
14
+ workerData: { dest },
15
+ sync: false
16
+ })
23
17
 
24
- let length = 0
18
+ let length = 0
25
19
 
26
- stream.on('close', () => {
27
- stat(dest, (err, f) => {
28
- t.error(err)
29
- t.equal(f.size, length)
30
- t.end()
20
+ stream.on('close', () => {
21
+ stat(dest, (err, f) => {
22
+ assert.ifError(err)
23
+ assert.strictEqual(f.size, length)
24
+ done()
25
+ })
31
26
  })
32
- })
33
27
 
34
- const buf = Buffer.alloc(1024).fill('x').toString() // 1 KB
28
+ const buf = Buffer.alloc(1024).fill('x').toString() // 1 KB
35
29
 
36
- // This writes 1 GB of data
37
- for (let i = 0; i < 1024 * 1024; i++) {
38
- length += buf.length
39
- stream.write(buf)
40
- }
30
+ // This writes 1 GB of data
31
+ for (let i = 0; i < 1024 * 1024; i++) {
32
+ length += buf.length
33
+ stream.write(buf)
34
+ }
41
35
 
42
- stream.end()
36
+ stream.end()
37
+ })