I found that strtok3.fromStream() tokenizers do not actually close the underlying Node.js Readable when tokenizer.close() is called.
Root cause:
lib/core.js wires fromStream() through StreamReader
lib/stream/StreamReader.js implements:
abort() as only this.reject(new AbortError())
close() as only return this.abort()
- Neither path calls
stream.destroy()
So tokenizer.close() only aborts pending tokenizer reads. It does not tear down the source stream itself.
Why this matters:
A consumer can successfully detect enough bytes, call await tokenizer.close(), and still leave the original Readable and socket alive in the background. For HTTP responses, that means the body keeps downloading after the caller is done with detection.
I reproduced this with file-type, which correctly calls await tokenizer.close() in a finally block. Without a local workaround, a Node IncomingMessage stays open after detection completes.
Expected behavior:
await tokenizer.close() for tokenizers created from Node streams should close or destroy the underlying Readable, so consumers do not need package-specific teardown workarounds after successful detection.
I found that
strtok3.fromStream()tokenizers do not actually close the underlying Node.jsReadablewhentokenizer.close()is called.Root cause:
lib/core.jswiresfromStream()throughStreamReaderlib/stream/StreamReader.jsimplements:abort()as onlythis.reject(new AbortError())close()as onlyreturn this.abort()stream.destroy()So
tokenizer.close()only aborts pending tokenizer reads. It does not tear down the source stream itself.Why this matters:
A consumer can successfully detect enough bytes, call
await tokenizer.close(), and still leave the originalReadableand socket alive in the background. For HTTP responses, that means the body keeps downloading after the caller is done with detection.I reproduced this with
file-type, which correctly callsawait tokenizer.close()in afinallyblock. Without a local workaround, a NodeIncomingMessagestays open after detection completes.Expected behavior:
await tokenizer.close()for tokenizers created from Node streams should close or destroy the underlyingReadable, so consumers do not need package-specific teardown workarounds after successful detection.