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.
- package/.github/workflows/ci.yml +3 -23
- package/CLAUDE.md +53 -0
- package/eslint.config.js +10 -0
- package/index.js +4 -0
- package/lib/wait.js +50 -43
- package/lib/worker.js +18 -13
- package/package.json +14 -19
- package/test/base.test.js +60 -86
- package/test/bench.test.js +3 -3
- package/test/bundlers.test.js +8 -9
- package/test/commonjs-fallback.test.js +17 -22
- package/test/context.test.js +6 -6
- package/test/end.test.js +22 -27
- package/test/esm.test.mjs +8 -9
- package/test/event.test.js +10 -9
- package/test/helper.js +1 -10
- package/test/indexes.test.js +4 -4
- package/test/multibyte-chars.test.mjs +14 -13
- package/test/pkg/index.js +23 -23
- package/test/pkg/pkg.config.json +3 -4
- package/test/pkg/pkg.test.js +5 -6
- package/test/post-message.test.js +4 -5
- package/test/string-limit-2.test.js +24 -30
- package/test/string-limit.test.js +24 -29
- package/test/thread-management.test.js +34 -17
- package/test/transpiled.test.js +5 -6
- package/test/ts-native.test.mjs +35 -0
- package/test/ts-node-fallback.test.js +35 -0
- package/.taprc +0 -4
- package/test/never-drain.test.js +0 -57
- package/test/ts.test.ts +0 -33
|
@@ -1,12 +1,7 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
const
|
|
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
|
-
|
|
19
|
-
|
|
20
|
-
const
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
buf
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
stream.
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
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
|
|
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
|
-
|
|
16
|
-
|
|
17
|
-
const
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
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
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
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
|
-
|
|
39
|
-
|
|
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
|
+
})
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
const { test } = require('
|
|
3
|
+
const { test } = require('node:test')
|
|
4
|
+
const assert = require('node:assert')
|
|
4
5
|
const { fork } = require('child_process')
|
|
5
6
|
const { join } = require('path')
|
|
6
7
|
const { readFile } = require('fs').promises
|
|
@@ -13,10 +14,10 @@ test('exits with 0', async function (t) {
|
|
|
13
14
|
const child = fork(join(__dirname, 'create-and-exit.js'), [dest])
|
|
14
15
|
|
|
15
16
|
const [code] = await once(child, 'exit')
|
|
16
|
-
|
|
17
|
+
assert.strictEqual(code, 0)
|
|
17
18
|
|
|
18
19
|
const data = await readFile(dest, 'utf8')
|
|
19
|
-
|
|
20
|
+
assert.strictEqual(data, 'hello world\n')
|
|
20
21
|
})
|
|
21
22
|
|
|
22
23
|
test('emit error if thread exits', async function (t) {
|
|
@@ -25,20 +26,24 @@ test('emit error if thread exits', async function (t) {
|
|
|
25
26
|
sync: true
|
|
26
27
|
})
|
|
27
28
|
|
|
29
|
+
const closed = once(stream, 'close').catch(() => {})
|
|
30
|
+
|
|
28
31
|
stream.on('ready', () => {
|
|
29
32
|
stream.write('hello world\n')
|
|
30
33
|
})
|
|
31
34
|
|
|
32
35
|
let [err] = await once(stream, 'error')
|
|
33
|
-
|
|
36
|
+
assert.strictEqual(err.message, 'the worker thread exited')
|
|
34
37
|
|
|
35
38
|
stream.write('noop');
|
|
36
39
|
[err] = await once(stream, 'error')
|
|
37
|
-
|
|
40
|
+
assert.strictEqual(err.message, 'the worker has exited')
|
|
38
41
|
|
|
39
42
|
stream.write('noop');
|
|
40
43
|
[err] = await once(stream, 'error')
|
|
41
|
-
|
|
44
|
+
assert.strictEqual(err.message, 'the worker has exited')
|
|
45
|
+
|
|
46
|
+
await closed
|
|
42
47
|
})
|
|
43
48
|
|
|
44
49
|
test('emit error if thread have unhandledRejection', async function (t) {
|
|
@@ -47,20 +52,24 @@ test('emit error if thread have unhandledRejection', async function (t) {
|
|
|
47
52
|
sync: true
|
|
48
53
|
})
|
|
49
54
|
|
|
55
|
+
const closed = once(stream, 'close').catch(() => {})
|
|
56
|
+
|
|
50
57
|
stream.on('ready', () => {
|
|
51
58
|
stream.write('hello world\n')
|
|
52
59
|
})
|
|
53
60
|
|
|
54
61
|
let [err] = await once(stream, 'error')
|
|
55
|
-
|
|
62
|
+
assert.strictEqual(err.message, 'kaboom')
|
|
56
63
|
|
|
57
64
|
stream.write('noop');
|
|
58
65
|
[err] = await once(stream, 'error')
|
|
59
|
-
|
|
66
|
+
assert.strictEqual(err.message, 'the worker has exited')
|
|
60
67
|
|
|
61
68
|
stream.write('noop');
|
|
62
69
|
[err] = await once(stream, 'error')
|
|
63
|
-
|
|
70
|
+
assert.strictEqual(err.message, 'the worker has exited')
|
|
71
|
+
|
|
72
|
+
await closed
|
|
64
73
|
})
|
|
65
74
|
|
|
66
75
|
test('emit error if worker stream emit error', async function (t) {
|
|
@@ -69,20 +78,24 @@ test('emit error if worker stream emit error', async function (t) {
|
|
|
69
78
|
sync: true
|
|
70
79
|
})
|
|
71
80
|
|
|
81
|
+
const closed = once(stream, 'close').catch(() => {})
|
|
82
|
+
|
|
72
83
|
stream.on('ready', () => {
|
|
73
84
|
stream.write('hello world\n')
|
|
74
85
|
})
|
|
75
86
|
|
|
76
87
|
let [err] = await once(stream, 'error')
|
|
77
|
-
|
|
88
|
+
assert.strictEqual(err.message, 'kaboom')
|
|
78
89
|
|
|
79
90
|
stream.write('noop');
|
|
80
91
|
[err] = await once(stream, 'error')
|
|
81
|
-
|
|
92
|
+
assert.strictEqual(err.message, 'the worker has exited')
|
|
82
93
|
|
|
83
94
|
stream.write('noop');
|
|
84
95
|
[err] = await once(stream, 'error')
|
|
85
|
-
|
|
96
|
+
assert.strictEqual(err.message, 'the worker has exited')
|
|
97
|
+
|
|
98
|
+
await closed
|
|
86
99
|
})
|
|
87
100
|
|
|
88
101
|
test('emit error if thread have uncaughtException', async function (t) {
|
|
@@ -91,20 +104,24 @@ test('emit error if thread have uncaughtException', async function (t) {
|
|
|
91
104
|
sync: true
|
|
92
105
|
})
|
|
93
106
|
|
|
107
|
+
const closed = once(stream, 'close').catch(() => {})
|
|
108
|
+
|
|
94
109
|
stream.on('ready', () => {
|
|
95
110
|
stream.write('hello world\n')
|
|
96
111
|
})
|
|
97
112
|
|
|
98
113
|
let [err] = await once(stream, 'error')
|
|
99
|
-
|
|
114
|
+
assert.strictEqual(err.message, 'kaboom')
|
|
100
115
|
|
|
101
116
|
stream.write('noop');
|
|
102
117
|
[err] = await once(stream, 'error')
|
|
103
|
-
|
|
118
|
+
assert.strictEqual(err.message, 'the worker has exited')
|
|
104
119
|
|
|
105
120
|
stream.write('noop');
|
|
106
121
|
[err] = await once(stream, 'error')
|
|
107
|
-
|
|
122
|
+
assert.strictEqual(err.message, 'the worker has exited')
|
|
123
|
+
|
|
124
|
+
await closed
|
|
108
125
|
})
|
|
109
126
|
|
|
110
127
|
test('close the work if out of scope on gc', { skip: !global.WeakRef }, async function (t) {
|
|
@@ -114,8 +131,8 @@ test('close the work if out of scope on gc', { skip: !global.WeakRef }, async fu
|
|
|
114
131
|
})
|
|
115
132
|
|
|
116
133
|
const [code] = await once(child, 'exit')
|
|
117
|
-
|
|
134
|
+
assert.strictEqual(code, 0)
|
|
118
135
|
|
|
119
136
|
const data = await readFile(dest, 'utf8')
|
|
120
|
-
|
|
137
|
+
assert.strictEqual(data, 'hello world\n')
|
|
121
138
|
})
|
package/test/transpiled.test.js
CHANGED
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
const { test } = require('
|
|
3
|
+
const { test } = require('node:test')
|
|
4
|
+
const assert = require('node:assert')
|
|
4
5
|
const { join } = require('path')
|
|
5
6
|
const { file } = require('./helper')
|
|
6
7
|
const ThreadStream = require('..')
|
|
7
8
|
|
|
8
9
|
function basic (esVersion) {
|
|
9
|
-
test(`transpiled-ts-to-${esVersion}`, function (
|
|
10
|
-
t.plan(2)
|
|
11
|
-
|
|
10
|
+
test(`transpiled-ts-to-${esVersion}`, function () {
|
|
12
11
|
const dest = file()
|
|
13
12
|
const stream = new ThreadStream({
|
|
14
13
|
filename: join(__dirname, 'ts', `to-file.${esVersion}.cjs`),
|
|
@@ -18,9 +17,9 @@ function basic (esVersion) {
|
|
|
18
17
|
|
|
19
18
|
// There are arbitrary checks, the important aspect of this test is to ensure
|
|
20
19
|
// that we can properly load the transpiled file into our worker thread.
|
|
21
|
-
|
|
20
|
+
assert.deepStrictEqual(stream.writableEnded, false)
|
|
22
21
|
stream.end()
|
|
23
|
-
|
|
22
|
+
assert.deepStrictEqual(stream.writableEnded, true)
|
|
24
23
|
})
|
|
25
24
|
}
|
|
26
25
|
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { test } from 'node:test'
|
|
2
|
+
import assert from 'node:assert'
|
|
3
|
+
import { readFile } from 'fs/promises'
|
|
4
|
+
import ThreadStream from '../index.js'
|
|
5
|
+
import { join } from 'desm'
|
|
6
|
+
import { file } from './helper.js'
|
|
7
|
+
|
|
8
|
+
const nodeVersion = parseInt(process.versions.node.split('.')[0], 10)
|
|
9
|
+
|
|
10
|
+
// Native TypeScript stripping (--experimental-strip-types) is only available in Node 22.6+
|
|
11
|
+
test('typescript module with native type stripping', { skip: nodeVersion < 22 }, async function (t) {
|
|
12
|
+
const dest = file()
|
|
13
|
+
const stream = new ThreadStream({
|
|
14
|
+
filename: join(import.meta.url, 'ts', 'to-file.ts'),
|
|
15
|
+
workerData: { dest },
|
|
16
|
+
workerOpts: {
|
|
17
|
+
execArgv: ['--experimental-strip-types', '--disable-warning=ExperimentalWarning']
|
|
18
|
+
},
|
|
19
|
+
sync: false
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
t.after(() => stream.end())
|
|
23
|
+
|
|
24
|
+
assert.ok(stream.write('hello world\n'))
|
|
25
|
+
assert.ok(stream.write('something else\n'))
|
|
26
|
+
|
|
27
|
+
stream.end()
|
|
28
|
+
|
|
29
|
+
await new Promise((resolve) => {
|
|
30
|
+
stream.on('close', resolve)
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
const data = await readFile(dest, 'utf8')
|
|
34
|
+
assert.strictEqual(data, 'hello world\nsomething else\n')
|
|
35
|
+
})
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const { test } = require('node:test')
|
|
4
|
+
const assert = require('node:assert')
|
|
5
|
+
const { readFile } = require('fs/promises')
|
|
6
|
+
const { join } = require('path')
|
|
7
|
+
const { file } = require('./helper')
|
|
8
|
+
const ThreadStream = require('..')
|
|
9
|
+
|
|
10
|
+
// This test verifies that TypeScript files can be loaded via ts-node
|
|
11
|
+
// when native type stripping is not enabled in the worker thread.
|
|
12
|
+
// Unlike ts.test.ts which passes --experimental-strip-types via execArgv,
|
|
13
|
+
// this test does NOT pass that flag, so the worker will fall back to ts-node.
|
|
14
|
+
test('typescript module with ts-node fallback', async function (t) {
|
|
15
|
+
const dest = file()
|
|
16
|
+
const stream = new ThreadStream({
|
|
17
|
+
filename: join(__dirname, 'ts', 'to-file.ts'),
|
|
18
|
+
workerData: { dest },
|
|
19
|
+
sync: false
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
t.after(() => stream.end())
|
|
23
|
+
|
|
24
|
+
assert.ok(stream.write('hello world\n'))
|
|
25
|
+
assert.ok(stream.write('something else\n'))
|
|
26
|
+
|
|
27
|
+
stream.end()
|
|
28
|
+
|
|
29
|
+
await new Promise((resolve) => {
|
|
30
|
+
stream.on('close', resolve)
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
const data = await readFile(dest, 'utf8')
|
|
34
|
+
assert.strictEqual(data, 'hello world\nsomething else\n')
|
|
35
|
+
})
|
package/.taprc
DELETED
package/test/never-drain.test.js
DELETED
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
const { test } = require('tap')
|
|
2
|
-
const ThreadStream = require('../index')
|
|
3
|
-
const { join } = require('path')
|
|
4
|
-
|
|
5
|
-
function retryUntilTimeout (fn, timeout) {
|
|
6
|
-
const start = Date.now()
|
|
7
|
-
return new Promise((resolve, reject) => {
|
|
8
|
-
async function run () {
|
|
9
|
-
if (fn()) {
|
|
10
|
-
resolve()
|
|
11
|
-
return
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
if (Date.now() - start >= timeout) {
|
|
15
|
-
reject(new Error('timeout'))
|
|
16
|
-
return
|
|
17
|
-
}
|
|
18
|
-
setTimeout(run, 10)
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
run()
|
|
22
|
-
})
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
const isNode18 = process.version.indexOf('v18') === 0
|
|
26
|
-
|
|
27
|
-
test('emit warning when the worker gracefully exit without the stream ended', { skip: !isNode18 }, async function (t) {
|
|
28
|
-
const expectedWarning = 'ThreadStream: process exited before destination stream was drained. this may indicate that the destination stream try to write to a another missing stream'
|
|
29
|
-
const stream = new ThreadStream({
|
|
30
|
-
filename: join(__dirname, 'to-next.js')
|
|
31
|
-
})
|
|
32
|
-
stream.unref()
|
|
33
|
-
|
|
34
|
-
let streamWarning
|
|
35
|
-
function saveWarning (e) {
|
|
36
|
-
if (e.message === expectedWarning) {
|
|
37
|
-
streamWarning = e
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
process.on('warning', saveWarning)
|
|
41
|
-
|
|
42
|
-
const data = 'hello'.repeat(10)
|
|
43
|
-
for (let i = 0; i < 1000; i++) {
|
|
44
|
-
if (streamWarning?.message === expectedWarning) {
|
|
45
|
-
break
|
|
46
|
-
}
|
|
47
|
-
stream.write(data)
|
|
48
|
-
await new Promise((resolve) => {
|
|
49
|
-
setTimeout(resolve, 1)
|
|
50
|
-
})
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
process.off('warning', saveWarning)
|
|
54
|
-
t.equal(streamWarning?.message, expectedWarning)
|
|
55
|
-
|
|
56
|
-
await retryUntilTimeout(() => stream.worker.exited === true, 3000)
|
|
57
|
-
})
|
package/test/ts.test.ts
DELETED
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
import { test } from 'tap'
|
|
2
|
-
import { readFile } from 'fs'
|
|
3
|
-
import ThreadStream from '../index.js'
|
|
4
|
-
import { join } from 'path'
|
|
5
|
-
import { file } from './helper.js'
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
test('typescript module', function (t) {
|
|
9
|
-
t.plan(5)
|
|
10
|
-
|
|
11
|
-
const dest = file()
|
|
12
|
-
const stream = new ThreadStream({
|
|
13
|
-
filename: join(__dirname, 'ts', 'to-file.ts'),
|
|
14
|
-
workerData: { dest },
|
|
15
|
-
sync: true
|
|
16
|
-
})
|
|
17
|
-
|
|
18
|
-
stream.on('finish', () => {
|
|
19
|
-
readFile(dest, 'utf8', (err, data) => {
|
|
20
|
-
t.error(err)
|
|
21
|
-
t.equal(data, 'hello world\nsomething else\n')
|
|
22
|
-
})
|
|
23
|
-
})
|
|
24
|
-
|
|
25
|
-
stream.on('close', () => {
|
|
26
|
-
t.pass('close emitted')
|
|
27
|
-
})
|
|
28
|
-
|
|
29
|
-
t.ok(stream.write('hello world\n'))
|
|
30
|
-
t.ok(stream.write('something else\n'))
|
|
31
|
-
|
|
32
|
-
stream.end()
|
|
33
|
-
})
|