or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

cookie-session.mdglobal-polyfills.mdindex.mdserver-runtime.mdsession-storage.mdstream-utilities.mdupload-handling.md

stream-utilities.mddocs/

0

# Stream Utilities

1

2

Utilities for converting between Node.js streams and Web Streams API, enabling seamless data flow between different stream types in Node.js environments.

3

4

## Capabilities

5

6

### Write ReadableStream to Writable

7

8

Writes a Web ReadableStream to a Node.js Writable stream with proper error handling and backpressure management.

9

10

```typescript { .api }

11

/**

12

* Writes a Web ReadableStream to a Node.js Writable stream

13

* @param stream - The ReadableStream to read from

14

* @param writable - The Node.js Writable stream to write to

15

* @returns Promise that resolves when the operation completes

16

*/

17

function writeReadableStreamToWritable(

18

stream: ReadableStream,

19

writable: Writable

20

): Promise<void>;

21

```

22

23

**Usage Examples:**

24

25

```typescript

26

import { writeReadableStreamToWritable } from "@remix-run/node";

27

import { createWriteStream } from "node:fs";

28

29

// Save a fetch response to file

30

export async function action({ request }: ActionFunctionArgs) {

31

const response = await fetch("https://example.com/large-file.zip");

32

33

if (response.body) {

34

const fileStream = createWriteStream("/tmp/downloaded-file.zip");

35

36

await writeReadableStreamToWritable(response.body, fileStream);

37

38

return json({ message: "File downloaded successfully" });

39

}

40

41

return json({ error: "No response body" }, { status: 400 });

42

}

43

44

// Stream request body to file

45

export async function action({ request }: ActionFunctionArgs) {

46

if (request.body) {

47

const outputStream = createWriteStream("/tmp/request-body.bin");

48

49

try {

50

await writeReadableStreamToWritable(request.body, outputStream);

51

return json({ success: true });

52

} catch (error) {

53

return json({ error: "Failed to save request body" }, { status: 500 });

54

}

55

}

56

57

return json({ error: "No request body" }, { status: 400 });

58

}

59

```

60

61

### Write AsyncIterable to Writable

62

63

Writes an AsyncIterable of Uint8Array chunks to a Node.js Writable stream.

64

65

```typescript { .api }

66

/**

67

* Writes an AsyncIterable to a Node.js Writable stream

68

* @param iterable - The AsyncIterable of Uint8Array chunks to write

69

* @param writable - The Node.js Writable stream to write to

70

* @returns Promise that resolves when the operation completes

71

*/

72

function writeAsyncIterableToWritable(

73

iterable: AsyncIterable<Uint8Array>,

74

writable: Writable

75

): Promise<void>;

76

```

77

78

**Usage Examples:**

79

80

```typescript

81

import { writeAsyncIterableToWritable } from "@remix-run/node";

82

import { createWriteStream } from "node:fs";

83

84

// Process and save async data

85

async function* generateData() {

86

for (let i = 0; i < 1000; i++) {

87

yield new TextEncoder().encode(`Data chunk ${i}\n`);

88

}

89

}

90

91

export async function action() {

92

const dataStream = generateData();

93

const fileStream = createWriteStream("/tmp/generated-data.txt");

94

95

await writeAsyncIterableToWritable(dataStream, fileStream);

96

97

return json({ message: "Data generated and saved" });

98

}

99

```

100

101

### ReadableStream to String

102

103

Converts a Web ReadableStream to a string with optional encoding specification.

104

105

```typescript { .api }

106

/**

107

* Converts a ReadableStream to a string

108

* @param stream - The ReadableStream to convert

109

* @param encoding - Optional buffer encoding (defaults to 'utf8')

110

* @returns Promise resolving to the stream contents as a string

111

*/

112

function readableStreamToString(

113

stream: ReadableStream<Uint8Array>,

114

encoding?: BufferEncoding

115

): Promise<string>;

116

```

117

118

**Usage Examples:**

119

120

```typescript

121

import { readableStreamToString } from "@remix-run/node";

122

123

// Read request body as string

124

export async function action({ request }: ActionFunctionArgs) {

125

if (request.body) {

126

const bodyText = await readableStreamToString(request.body);

127

128

try {

129

const data = JSON.parse(bodyText);

130

return json({ received: data });

131

} catch (error) {

132

return json({ error: "Invalid JSON" }, { status: 400 });

133

}

134

}

135

136

return json({ error: "No request body" }, { status: 400 });

137

}

138

139

// Read fetch response as string

140

export async function loader() {

141

const response = await fetch("https://api.example.com/data");

142

143

if (response.body) {

144

const text = await readableStreamToString(response.body);

145

return json({ data: text });

146

}

147

148

return json({ error: "No response body" }, { status: 500 });

149

}

150

151

// Read with specific encoding

152

export async function action({ request }: ActionFunctionArgs) {

153

if (request.body) {

154

const bodyText = await readableStreamToString(request.body, "base64");

155

return json({ encodedData: bodyText });

156

}

157

158

return json({ error: "No request body" }, { status: 400 });

159

}

160

```

161

162

### Create ReadableStream from Readable

163

164

Converts a Node.js Readable stream to a Web ReadableStream with proper backpressure handling.

165

166

```typescript { .api }

167

/**

168

* Creates a Web ReadableStream from a Node.js Readable stream

169

* @param source - The Node.js Readable stream to convert

170

* @returns Web ReadableStream

171

*/

172

function createReadableStreamFromReadable(

173

source: Readable & { readableHighWaterMark?: number }

174

): ReadableStream;

175

```

176

177

**Usage Examples:**

178

179

```typescript

180

import { createReadableStreamFromReadable } from "@remix-run/node";

181

import { createReadStream } from "node:fs";

182

183

// Stream file contents as Web ReadableStream

184

export async function loader({ params }: LoaderFunctionArgs) {

185

const filePath = `/files/${params.filename}`;

186

const nodeStream = createReadStream(filePath);

187

const webStream = createReadableStreamFromReadable(nodeStream);

188

189

return new Response(webStream, {

190

headers: {

191

"Content-Type": "application/octet-stream",

192

"Content-Disposition": `attachment; filename="${params.filename}"`

193

}

194

});

195

}

196

197

// Convert process.stdin to Web ReadableStream

198

export async function action() {

199

const stdinStream = createReadableStreamFromReadable(process.stdin);

200

const reader = stdinStream.getReader();

201

202

const chunks: string[] = [];

203

while (true) {

204

const { done, value } = await reader.read();

205

if (done) break;

206

207

chunks.push(new TextDecoder().decode(value));

208

}

209

210

return json({ input: chunks.join("") });

211

}

212

```

213

214

## Stream Conversion Patterns

215

216

**Node.js to Web Streams:**

217

218

```typescript

219

import { createReadableStreamFromReadable } from "@remix-run/node";

220

import { createReadStream } from "node:fs";

221

222

// File → Web ReadableStream

223

const fileStream = createReadStream("data.txt");

224

const webStream = createReadableStreamFromReadable(fileStream);

225

```

226

227

**Web Streams to Node.js:**

228

229

```typescript

230

import { writeReadableStreamToWritable, readableStreamToString } from "@remix-run/node";

231

import { createWriteStream } from "node:fs";

232

233

// Web ReadableStream → File

234

const response = await fetch("https://example.com/data");

235

if (response.body) {

236

await writeReadableStreamToWritable(response.body, createWriteStream("output.txt"));

237

}

238

239

// Web ReadableStream → String

240

const text = await readableStreamToString(response.body);

241

```

242

243

**Performance Considerations:**

244

245

- **Backpressure handling**: All utilities properly handle backpressure to prevent memory issues

246

- **Streaming processing**: Data is processed in chunks to handle large streams efficiently

247

- **Error propagation**: Errors are properly propagated between stream types

248

- **Resource cleanup**: Streams are properly closed and cleaned up on completion or error