or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

cli.mdfile-system.mdindex.mdnetwork-operations.mdprocess-management.mdshell-execution.mduser-interaction.mdutilities.md

network-operations.mddocs/

0

# Network Operations

1

2

HTTP operations with fetch API and piping support for data streaming and network requests in scripts.

3

4

## Capabilities

5

6

### HTTP Fetch

7

8

Enhanced fetch implementation with piping support for streaming data directly to commands or files.

9

10

```typescript { .api }

11

/**

12

* Fetch HTTP resources with piping support

13

* @param url - URL to fetch or Request object

14

* @param init - Request configuration options

15

* @returns Promise resolving to Response with pipe method

16

*/

17

function fetch(

18

url: RequestInfo,

19

init?: RequestInit

20

): Promise<Response> & {

21

pipe: {

22

/** Pipe response to shell command */

23

(dest: TemplateStringsArray, ...args: any[]): ProcessPromise;

24

/** Pipe response to destination */

25

<D>(dest: D): D;

26

};

27

};

28

```

29

30

**Basic Usage:**

31

32

```typescript

33

import { fetch, echo } from "zx";

34

35

// Simple HTTP request

36

const response = await fetch('https://api.github.com/user', {

37

headers: {

38

'Authorization': 'token ghp_xxxxxxxxxxxx',

39

'User-Agent': 'MyScript/1.0'

40

}

41

});

42

43

const userData = await response.json();

44

echo`User: ${userData.login}`;

45

46

// POST request

47

const createResponse = await fetch('https://api.example.com/items', {

48

method: 'POST',

49

headers: {

50

'Content-Type': 'application/json',

51

},

52

body: JSON.stringify({

53

name: 'New Item',

54

description: 'Created from script'

55

})

56

});

57

58

if (createResponse.ok) {

59

echo('Item created successfully');

60

} else {

61

echo`Error: ${createResponse.status} ${createResponse.statusText}`;

62

}

63

```

64

65

### Streaming and Piping

66

67

Stream HTTP responses directly to commands or files without loading into memory.

68

69

```typescript { .api }

70

interface FetchWithPipe extends Promise<Response> {

71

pipe: {

72

/** Pipe response data to shell command */

73

(dest: TemplateStringsArray, ...args: any[]): ProcessPromise;

74

/** Pipe response data to any destination */

75

<D>(dest: D): D;

76

};

77

}

78

```

79

80

**Piping Examples:**

81

82

```typescript

83

import { fetch, $ } from "zx";

84

85

// Download and pipe to file

86

await fetch('https://releases.ubuntu.com/20.04/ubuntu-20.04.6-desktop-amd64.iso')

87

.pipe('./ubuntu.iso');

88

89

// Download and pipe through commands

90

await fetch('https://api.github.com/repos/microsoft/vscode/releases')

91

.pipe`jq '.[0].tag_name'`

92

.pipe('./latest-version.txt');

93

94

// Stream processing

95

const logProcessor = fetch('https://logs.example.com/app.log')

96

.pipe`grep "ERROR"`

97

.pipe`wc -l`;

98

99

const errorCount = await logProcessor;

100

echo`Found ${errorCount.stdout.trim()} errors in logs`;

101

102

// Multiple pipe operations

103

await fetch('https://raw.githubusercontent.com/user/repo/main/data.csv')

104

.pipe`head -100` // First 100 lines

105

.pipe`cut -d',' -f1,3` // Extract columns 1 and 3

106

.pipe`sort` // Sort the data

107

.pipe('./processed-data.csv'); // Save to file

108

```

109

110

### Response Processing

111

112

Handle different response types and status codes.

113

114

**Examples:**

115

116

```typescript

117

import { fetch, echo, $ } from "zx";

118

119

// JSON API responses

120

const apiResponse = await fetch('https://api.github.com/repos/google/zx');

121

if (apiResponse.ok) {

122

const repo = await apiResponse.json();

123

echo`Repository: ${repo.full_name}`;

124

echo`Stars: ${repo.stargazers_count}`;

125

echo`Language: ${repo.language}`;

126

} else {

127

echo`API Error: ${apiResponse.status}`;

128

}

129

130

// Text responses

131

const readmeResponse = await fetch('https://raw.githubusercontent.com/google/zx/main/README.md');

132

const readmeText = await readmeResponse.text();

133

echo`README length: ${readmeText.length} characters`;

134

135

// Binary data

136

const imageResponse = await fetch('https://github.com/google/zx/raw/main/docs/img/logo.svg');

137

const imageBuffer = await imageResponse.arrayBuffer();

138

echo`Image size: ${imageBuffer.byteLength} bytes`;

139

140

// Stream processing

141

const largeFileResponse = await fetch('https://example.com/large-dataset.json');

142

const reader = largeFileResponse.body?.getReader();

143

144

if (reader) {

145

let chunks = 0;

146

let totalBytes = 0;

147

148

while (true) {

149

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

150

if (done) break;

151

152

chunks++;

153

totalBytes += value.length;

154

155

if (chunks % 100 === 0) {

156

echo`Processed ${chunks} chunks, ${totalBytes} bytes`;

157

}

158

}

159

}

160

```

161

162

### Request Configuration

163

164

Configure requests with headers, authentication, and other options.

165

166

**Examples:**

167

168

```typescript

169

import { fetch, echo, argv } from "zx";

170

171

// With authentication

172

const apiKey = argv.apiKey || process.env.API_KEY;

173

const authenticatedResponse = await fetch('https://api.example.com/protected', {

174

headers: {

175

'Authorization': `Bearer ${apiKey}`,

176

'Content-Type': 'application/json'

177

}

178

});

179

180

// With timeout using AbortController

181

const controller = new AbortController();

182

const timeoutId = setTimeout(() => controller.abort(), 10000); // 10 second timeout

183

184

try {

185

const response = await fetch('https://slow-api.example.com/data', {

186

signal: controller.signal

187

});

188

clearTimeout(timeoutId);

189

190

const data = await response.json();

191

echo`Received data: ${JSON.stringify(data, null, 2)}`;

192

} catch (error) {

193

if (error.name === 'AbortError') {

194

echo('Request timed out');

195

} else {

196

echo`Request failed: ${error.message}`;

197

}

198

}

199

200

// Form data submission

201

const formData = new FormData();

202

formData.append('file', './upload.txt');

203

formData.append('description', 'Uploaded from script');

204

205

const uploadResponse = await fetch('https://api.example.com/upload', {

206

method: 'POST',

207

body: formData

208

});

209

210

if (uploadResponse.ok) {

211

echo('File uploaded successfully');

212

}

213

214

// Custom request headers

215

const customResponse = await fetch('https://api.example.com/data', {

216

headers: {

217

'User-Agent': 'ZX-Script/1.0',

218

'Accept': 'application/json',

219

'X-Custom-Header': 'custom-value'

220

}

221

});

222

```

223

224

### Error Handling and Retry

225

226

Handle network errors and implement retry logic.

227

228

**Examples:**

229

230

```typescript

231

import { fetch, echo, retry, sleep } from "zx";

232

233

// Simple retry on failure

234

const dataWithRetry = await retry(3, async () => {

235

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

236

if (!response.ok) {

237

throw new Error(`HTTP ${response.status}: ${response.statusText}`);

238

}

239

return response.json();

240

});

241

242

// Retry with exponential backoff

243

const reliableData = await retry(5, '1s', async () => {

244

try {

245

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

246

247

if (response.status === 429) { // Rate limited

248

throw new Error('Rate limited, will retry');

249

}

250

251

if (!response.ok) {

252

throw new Error(`HTTP ${response.status}`);

253

}

254

255

return response.json();

256

} catch (error) {

257

echo`Fetch failed: ${error.message}, retrying...`;

258

throw error;

259

}

260

});

261

262

// Health check with timeout

263

async function checkServiceHealth(url) {

264

const controller = new AbortController();

265

const timeout = setTimeout(() => controller.abort(), 5000);

266

267

try {

268

const response = await fetch(`${url}/health`, {

269

signal: controller.signal

270

});

271

clearTimeout(timeout);

272

273

return response.ok;

274

} catch (error) {

275

clearTimeout(timeout);

276

return false;

277

}

278

}

279

280

const services = ['https://api1.example.com', 'https://api2.example.com'];

281

for (const service of services) {

282

const healthy = await checkServiceHealth(service);

283

echo`${service}: ${healthy ? 'healthy' : 'unhealthy'}`;

284

}

285

```

286

287

### Working with APIs

288

289

Common patterns for working with REST APIs and webhooks.

290

291

**Examples:**

292

293

```typescript

294

import { fetch, echo, question, argv } from "zx";

295

296

// GitHub API integration

297

async function getLatestRelease(repo) {

298

const response = await fetch(`https://api.github.com/repos/${repo}/releases/latest`);

299

if (!response.ok) {

300

throw new Error(`Failed to get release info: ${response.status}`);

301

}

302

return response.json();

303

}

304

305

const release = await getLatestRelease('google/zx');

306

echo`Latest ZX version: ${release.tag_name}`;

307

echo`Published: ${new Date(release.published_at).toLocaleDateString()}`;

308

309

// Webhook notifications

310

async function sendSlackNotification(webhook, message) {

311

const response = await fetch(webhook, {

312

method: 'POST',

313

headers: { 'Content-Type': 'application/json' },

314

body: JSON.stringify({ text: message })

315

});

316

317

return response.ok;

318

}

319

320

const webhookUrl = argv.slackWebhook;

321

if (webhookUrl) {

322

await sendSlackNotification(webhookUrl, 'Deployment completed successfully! ๐Ÿš€');

323

}

324

325

// REST API CRUD operations

326

const baseUrl = 'https://api.example.com';

327

const apiKey = process.env.API_KEY;

328

329

// Create

330

const newUser = await fetch(`${baseUrl}/users`, {

331

method: 'POST',

332

headers: {

333

'Authorization': `Bearer ${apiKey}`,

334

'Content-Type': 'application/json'

335

},

336

body: JSON.stringify({

337

name: 'John Doe',

338

email: 'john@example.com'

339

})

340

});

341

342

// Read

343

const users = await fetch(`${baseUrl}/users?limit=10`, {

344

headers: { 'Authorization': `Bearer ${apiKey}` }

345

});

346

347

// Update

348

const updatedUser = await fetch(`${baseUrl}/users/123`, {

349

method: 'PATCH',

350

headers: {

351

'Authorization': `Bearer ${apiKey}`,

352

'Content-Type': 'application/json'

353

},

354

body: JSON.stringify({ status: 'active' })

355

});

356

357

// Delete

358

const deleteResult = await fetch(`${baseUrl}/users/123`, {

359

method: 'DELETE',

360

headers: { 'Authorization': `Bearer ${apiKey}` }

361

});

362

```

363

364

## Types

365

366

```typescript { .api }

367

/**

368

* Request URL or Request object

369

*/

370

type RequestInfo = string | URL | Request;

371

372

/**

373

* Request configuration options

374

*/

375

interface RequestInit {

376

method?: string;

377

headers?: HeadersInit;

378

body?: BodyInit | null;

379

mode?: RequestMode;

380

credentials?: RequestCredentials;

381

cache?: RequestCache;

382

redirect?: RequestRedirect;

383

referrer?: string;

384

referrerPolicy?: ReferrerPolicy;

385

integrity?: string;

386

keepalive?: boolean;

387

signal?: AbortSignal | null;

388

}

389

390

/**

391

* HTTP response object

392

*/

393

interface Response extends Body {

394

readonly headers: Headers;

395

readonly ok: boolean;

396

readonly redirected: boolean;

397

readonly status: number;

398

readonly statusText: string;

399

readonly type: ResponseType;

400

readonly url: string;

401

402

clone(): Response;

403

}

404

405

/**

406

* Response body methods

407

*/

408

interface Body {

409

readonly body: ReadableStream<Uint8Array> | null;

410

readonly bodyUsed: boolean;

411

412

arrayBuffer(): Promise<ArrayBuffer>;

413

blob(): Promise<Blob>;

414

formData(): Promise<FormData>;

415

json(): Promise<any>;

416

text(): Promise<string>;

417

}

418

```