Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .changeset/fix-stream-mkuint8array-mutable-fold.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"effect": patch
---

Fix `Stream.mkUint8Array` crash in Bun compiled+minified binaries.

Reverted to the pre-beta.59 immutable `Channel.runFold` pattern where each fold iteration returns a new `Uint8Array` instead of mutating a shared accumulator. The beta.59 refactor introduced in-place mutation (`acc.bytes +=`, `acc.arrays.push`) which throws in Bun `--compile --minify` binaries.
37 changes: 13 additions & 24 deletions packages/effect/src/Stream.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10339,31 +10339,20 @@ export const mkString = <E, R>(self: Stream<string, E, R>): Effect.Effect<string
* @category Destructors
*/
export const mkUint8Array = <E, R>(self: Stream<Uint8Array, E, R>): Effect.Effect<Uint8Array, E, R> =>
Effect.map(
Channel.runFold(
self.channel,
(): {
bytes: number
readonly arrays: Array<Uint8Array>
} => ({
bytes: 0,
arrays: []
}),
(acc, chunk) => {
for (let i = 0; i < chunk.length; i++) {
acc.bytes += chunk[i].length
acc.arrays.push(chunk[i])
}
return acc
Channel.runFold(
self.channel,
() => new Uint8Array(0),
(acc, chunk) => {
let chunkLength = 0
for (let i = 0; i < chunk.length; i++) {
chunkLength += chunk[i].length
}
),
({ arrays, bytes }) => {
const result = new Uint8Array(bytes)
let offset = 0
for (let i = 0; i < arrays.length; i++) {
const array = arrays[i]
result.set(array, offset)
offset += array.length
const result = new Uint8Array(acc.length + chunkLength)
result.set(acc, 0)
let offset = acc.length
for (let i = 0; i < chunk.length; i++) {
result.set(chunk[i], offset)
offset += chunk[i].length
}
return result
}
Expand Down
23 changes: 23 additions & 0 deletions packages/effect/test/Stream.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,29 @@ describe("Stream", () => {
}))
})

describe("mkUint8Array", () => {
it.effect("concatenates chunks into a single Uint8Array", () =>
Effect.gen(function*() {
const result = yield* Stream.mkUint8Array(
Stream.make(new Uint8Array([1, 2]), new Uint8Array([3, 4]))
)
assert.deepStrictEqual(result, new Uint8Array([1, 2, 3, 4]))
}))

it.effect("works with many chunks (regression: mutable accumulator in Bun compiled binaries)", () =>
Effect.gen(function*() {
const chunks = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9].map((i) => new Uint8Array([i]))
const result = yield* Stream.mkUint8Array(Stream.fromIterable(chunks))
assert.deepStrictEqual(result, new Uint8Array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]))
}))

it.effect("handles empty stream", () =>
Effect.gen(function*() {
const result = yield* Stream.mkUint8Array(Stream.empty)
assert.deepStrictEqual(result, new Uint8Array([]))
}))
})

describe("encoding", () => {
it.effect("decodeText handles multi-byte characters split across chunks", () =>
Effect.gen(function*() {
Expand Down
Loading