or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

buffer-writing.mdindex.mdindexed-reading.mdindexing.mditeration.mdreading.mdwriting.md
tile.json

writing.mddocs/

0

# CAR Writing

1

2

Streaming writer interface for creating CAR archives with backpressure support, flexible root management, and efficient data output. CarWriter provides a channel-based architecture separating the writing interface from data output.

3

4

## Capabilities

5

6

### CarWriter Class

7

8

Provides streaming CAR archive creation with backpressure and channel-based output.

9

10

```typescript { .api }

11

/**

12

* Streaming CAR writer with backpressure support

13

* Uses channel architecture: writer for input, out for data stream

14

*/

15

class CarWriter {

16

/** Write a Block to the archive, resolves when data is written to output stream */

17

put(block: Block): Promise<void>;

18

19

/** Finalize the CAR archive and signal output stream completion */

20

close(): Promise<void>;

21

22

/** Create new writer channel with specified roots */

23

static create(roots?: CID[] | CID): WriterChannel;

24

25

/** Create appender channel (no header, for extending existing CARs) */

26

static createAppender(): WriterChannel;

27

28

/** Update roots in existing CAR byte array (must be same header length) */

29

static updateRootsInBytes(bytes: Uint8Array, roots: CID[]): Promise<Uint8Array>;

30

}

31

32

/**

33

* Writer channel containing writer instance and output stream

34

*/

35

interface WriterChannel {

36

/** Writer instance for putting blocks */

37

writer: CarWriter;

38

/** Output stream yielding CAR bytes */

39

out: AsyncIterable<Uint8Array>;

40

}

41

```

42

43

**Usage Examples:**

44

45

```typescript

46

import { CarWriter } from "@ipld/car/writer";

47

import fs from 'fs';

48

import { Readable } from 'stream';

49

import { CID } from 'multiformats/cid';

50

51

// Create CAR with single root

52

const { writer, out } = CarWriter.create(rootCid);

53

54

// Pipe output to file

55

Readable.from(out).pipe(fs.createWriteStream('output.car'));

56

57

// Write blocks

58

await writer.put({ cid: block1Cid, bytes: block1Data });

59

await writer.put({ cid: block2Cid, bytes: block2Data });

60

61

// Finalize

62

await writer.close();

63

64

// Create CAR with multiple roots

65

const { writer: multiWriter, out: multiOut } = CarWriter.create([root1, root2, root3]);

66

67

// Create CAR with no initial roots (can be set later)

68

const { writer: emptyWriter, out: emptyOut } = CarWriter.create();

69

```

70

71

### CAR Appending

72

73

Create appender for extending existing CAR archives without header.

74

75

```typescript { .api }

76

/**

77

* Create appender channel for extending existing CAR archives

78

* Does not write header - designed to append to existing CAR

79

*/

80

static createAppender(): WriterChannel;

81

```

82

83

**Usage Example:**

84

85

```typescript

86

import { CarWriter } from "@ipld/car/writer";

87

import fs from 'fs';

88

89

// Create appender (no header)

90

const { writer, out } = CarWriter.createAppender();

91

92

// Append to existing file

93

const appendStream = fs.createWriteStream('existing.car', { flags: 'a' });

94

Readable.from(out).pipe(appendStream);

95

96

// Add more blocks to existing CAR

97

await writer.put({ cid: newBlockCid, bytes: newBlockData });

98

await writer.close();

99

```

100

101

### Root Management

102

103

Update roots in existing CAR files with same-length header constraint.

104

105

```typescript { .api }

106

/**

107

* Update roots in existing CAR byte array

108

* New header must be exactly same length as existing header

109

* @param bytes - CAR byte array to modify (modified in place)

110

* @param roots - New roots (must encode to same byte length)

111

* @returns Modified byte array

112

*/

113

static updateRootsInBytes(bytes: Uint8Array, roots: CID[]): Promise<Uint8Array>;

114

```

115

116

**Usage Example:**

117

118

```typescript

119

import { CarWriter } from "@ipld/car/writer";

120

import fs from 'fs';

121

122

// Load existing CAR

123

const carBytes = fs.readFileSync('existing.car');

124

125

// Update roots (must be same encoded length)

126

const newRoots = [newRootCid]; // Must encode to same byte length as old roots

127

const updatedCar = await CarWriter.updateRootsInBytes(carBytes, newRoots);

128

129

// Write updated CAR

130

fs.writeFileSync('updated.car', updatedCar);

131

```

132

133

### File-Based Root Updates (Node.js Only)

134

135

Update roots directly in CAR files using file descriptors.

136

137

```typescript { .api }

138

/**

139

* Update roots in CAR file using file descriptor (Node.js only)

140

* File must be opened in read+write mode ('r+')

141

* @param fd - File descriptor (number or FileHandle)

142

* @param roots - New roots (must encode to same byte length as existing)

143

*/

144

static updateRootsInFile(

145

fd: fs.promises.FileHandle | number,

146

roots: CID[]

147

): Promise<void>;

148

```

149

150

**Usage Example:**

151

152

```typescript

153

import fs from 'fs';

154

import { CarWriter } from "@ipld/car/writer";

155

156

// Open file in read+write mode

157

const fd = await fs.promises.open('existing.car', 'r+');

158

159

try {

160

// Update roots in place

161

await CarWriter.updateRootsInFile(fd, [newRoot1, newRoot2]);

162

console.log('Roots updated successfully');

163

} catch (error) {

164

if (error.message.includes('same length')) {

165

console.log('New roots must encode to same byte length as existing roots');

166

}

167

} finally {

168

await fd.close();

169

}

170

```

171

172

### Backpressure Handling

173

174

CarWriter provides backpressure through Promise resolution timing.

175

176

```typescript

177

// Wait for each block to be written to output stream

178

await writer.put(block1); // Resolves when block1 data is consumed from 'out'

179

await writer.put(block2); // Resolves when block2 data is consumed from 'out'

180

181

// Alternative: Queue operations (memory will accumulate)

182

const promises = [

183

writer.put(block1),

184

writer.put(block2),

185

writer.put(block3)

186

];

187

// Data will queue in memory until 'out' stream is consumed

188

await Promise.all(promises);

189

```

190

191

### Error Handling

192

193

Common errors when writing CAR files:

194

195

- **TypeError**: Invalid block format (must have `cid` and `bytes` properties)

196

- **Error**: Writer already closed, invalid CID format

197

- **RangeError**: Header length mismatch when updating roots

198

199

```typescript

200

try {

201

await writer.put({ cid: invalidCid, bytes: data });

202

} catch (error) {

203

if (error instanceof TypeError) {

204

console.log('Invalid block format');

205

} else if (error.message.includes('closed')) {

206

console.log('Writer already closed');

207

}

208

}

209

210

try {

211

await CarWriter.updateRootsInBytes(carBytes, newRoots);

212

} catch (error) {

213

if (error.message.includes('same length')) {

214

console.log('New header must be same length as existing header');

215

}

216

}

217

```

218

219

## Platform Differences

220

221

### Browser Environment

222

- Uses `CarWriter` from `lib/writer-browser.js`

223

- Memory and stream-based operations only

224

- No file descriptor support

225

226

### Node.js Environment

227

- Uses `CarWriter` from `lib/writer.js` (extends browser version)

228

- Adds `updateRootsInFile()` static method

229

- Supports file descriptor operations