CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-zx

A tool for writing better scripts by bridging JavaScript and shell commands with cross-platform wrappers around child_process

Overview
Eval results
Files

network-operations.mddocs/

Network Operations

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

Capabilities

HTTP Fetch

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

/**
 * Fetch HTTP resources with piping support
 * @param url - URL to fetch or Request object
 * @param init - Request configuration options
 * @returns Promise resolving to Response with pipe method
 */
function fetch(
  url: RequestInfo,
  init?: RequestInit
): Promise<Response> & {
  pipe: {
    /** Pipe response to shell command */
    (dest: TemplateStringsArray, ...args: any[]): ProcessPromise;
    /** Pipe response to destination */
    <D>(dest: D): D;
  };
};

Basic Usage:

import { fetch, echo } from "zx";

// Simple HTTP request
const response = await fetch('https://api.github.com/user', {
  headers: {
    'Authorization': 'token ghp_xxxxxxxxxxxx',
    'User-Agent': 'MyScript/1.0'
  }
});

const userData = await response.json();
echo`User: ${userData.login}`;

// POST request
const createResponse = await fetch('https://api.example.com/items', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    name: 'New Item',
    description: 'Created from script'
  })
});

if (createResponse.ok) {
  echo('Item created successfully');
} else {
  echo`Error: ${createResponse.status} ${createResponse.statusText}`;
}

Streaming and Piping

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

interface FetchWithPipe extends Promise<Response> {
  pipe: {
    /** Pipe response data to shell command */
    (dest: TemplateStringsArray, ...args: any[]): ProcessPromise;
    /** Pipe response data to any destination */
    <D>(dest: D): D;
  };
}

Piping Examples:

import { fetch, $ } from "zx";

// Download and pipe to file
await fetch('https://releases.ubuntu.com/20.04/ubuntu-20.04.6-desktop-amd64.iso')
  .pipe('./ubuntu.iso');

// Download and pipe through commands
await fetch('https://api.github.com/repos/microsoft/vscode/releases')
  .pipe`jq '.[0].tag_name'`
  .pipe('./latest-version.txt');

// Stream processing
const logProcessor = fetch('https://logs.example.com/app.log')
  .pipe`grep "ERROR"`
  .pipe`wc -l`;

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

// Multiple pipe operations
await fetch('https://raw.githubusercontent.com/user/repo/main/data.csv')
  .pipe`head -100`                    // First 100 lines
  .pipe`cut -d',' -f1,3`             // Extract columns 1 and 3
  .pipe`sort`                        // Sort the data
  .pipe('./processed-data.csv');     // Save to file

Response Processing

Handle different response types and status codes.

Examples:

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

// JSON API responses
const apiResponse = await fetch('https://api.github.com/repos/google/zx');
if (apiResponse.ok) {
  const repo = await apiResponse.json();
  echo`Repository: ${repo.full_name}`;
  echo`Stars: ${repo.stargazers_count}`;
  echo`Language: ${repo.language}`;
} else {
  echo`API Error: ${apiResponse.status}`;
}

// Text responses
const readmeResponse = await fetch('https://raw.githubusercontent.com/google/zx/main/README.md');
const readmeText = await readmeResponse.text();
echo`README length: ${readmeText.length} characters`;

// Binary data
const imageResponse = await fetch('https://github.com/google/zx/raw/main/docs/img/logo.svg');
const imageBuffer = await imageResponse.arrayBuffer();
echo`Image size: ${imageBuffer.byteLength} bytes`;

// Stream processing
const largeFileResponse = await fetch('https://example.com/large-dataset.json');
const reader = largeFileResponse.body?.getReader();

if (reader) {
  let chunks = 0;
  let totalBytes = 0;
  
  while (true) {
    const { done, value } = await reader.read();
    if (done) break;
    
    chunks++;
    totalBytes += value.length;
    
    if (chunks % 100 === 0) {
      echo`Processed ${chunks} chunks, ${totalBytes} bytes`;
    }
  }
}

Request Configuration

Configure requests with headers, authentication, and other options.

Examples:

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

// With authentication
const apiKey = argv.apiKey || process.env.API_KEY;
const authenticatedResponse = await fetch('https://api.example.com/protected', {
  headers: {
    'Authorization': `Bearer ${apiKey}`,
    'Content-Type': 'application/json'
  }
});

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

try {
  const response = await fetch('https://slow-api.example.com/data', {
    signal: controller.signal
  });
  clearTimeout(timeoutId);
  
  const data = await response.json();
  echo`Received data: ${JSON.stringify(data, null, 2)}`;
} catch (error) {
  if (error.name === 'AbortError') {
    echo('Request timed out');
  } else {
    echo`Request failed: ${error.message}`;
  }
}

// Form data submission
const formData = new FormData();
formData.append('file', './upload.txt');
formData.append('description', 'Uploaded from script');

const uploadResponse = await fetch('https://api.example.com/upload', {
  method: 'POST',
  body: formData
});

if (uploadResponse.ok) {
  echo('File uploaded successfully');
}

// Custom request headers
const customResponse = await fetch('https://api.example.com/data', {
  headers: {
    'User-Agent': 'ZX-Script/1.0',
    'Accept': 'application/json',
    'X-Custom-Header': 'custom-value'
  }
});

Error Handling and Retry

Handle network errors and implement retry logic.

Examples:

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

// Simple retry on failure
const dataWithRetry = await retry(3, async () => {
  const response = await fetch('https://unreliable-api.example.com/data');
  if (!response.ok) {
    throw new Error(`HTTP ${response.status}: ${response.statusText}`);
  }
  return response.json();
});

// Retry with exponential backoff
const reliableData = await retry(5, '1s', async () => {
  try {
    const response = await fetch('https://api.example.com/critical-data');
    
    if (response.status === 429) { // Rate limited
      throw new Error('Rate limited, will retry');
    }
    
    if (!response.ok) {
      throw new Error(`HTTP ${response.status}`);
    }
    
    return response.json();
  } catch (error) {
    echo`Fetch failed: ${error.message}, retrying...`;
    throw error;
  }
});

// Health check with timeout
async function checkServiceHealth(url) {
  const controller = new AbortController();
  const timeout = setTimeout(() => controller.abort(), 5000);
  
  try {
    const response = await fetch(`${url}/health`, {
      signal: controller.signal
    });
    clearTimeout(timeout);
    
    return response.ok;
  } catch (error) {
    clearTimeout(timeout);
    return false;
  }
}

const services = ['https://api1.example.com', 'https://api2.example.com'];
for (const service of services) {
  const healthy = await checkServiceHealth(service);
  echo`${service}: ${healthy ? 'healthy' : 'unhealthy'}`;
}

Working with APIs

Common patterns for working with REST APIs and webhooks.

Examples:

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

// GitHub API integration
async function getLatestRelease(repo) {
  const response = await fetch(`https://api.github.com/repos/${repo}/releases/latest`);
  if (!response.ok) {
    throw new Error(`Failed to get release info: ${response.status}`);
  }
  return response.json();
}

const release = await getLatestRelease('google/zx');
echo`Latest ZX version: ${release.tag_name}`;
echo`Published: ${new Date(release.published_at).toLocaleDateString()}`;

// Webhook notifications
async function sendSlackNotification(webhook, message) {
  const response = await fetch(webhook, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ text: message })
  });
  
  return response.ok;
}

const webhookUrl = argv.slackWebhook;
if (webhookUrl) {
  await sendSlackNotification(webhookUrl, 'Deployment completed successfully! 🚀');
}

// REST API CRUD operations
const baseUrl = 'https://api.example.com';
const apiKey = process.env.API_KEY;

// Create
const newUser = await fetch(`${baseUrl}/users`, {
  method: 'POST',
  headers: {
    'Authorization': `Bearer ${apiKey}`,
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    name: 'John Doe',
    email: 'john@example.com'
  })
});

// Read
const users = await fetch(`${baseUrl}/users?limit=10`, {
  headers: { 'Authorization': `Bearer ${apiKey}` }
});

// Update
const updatedUser = await fetch(`${baseUrl}/users/123`, {
  method: 'PATCH',
  headers: {
    'Authorization': `Bearer ${apiKey}`,
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({ status: 'active' })
});

// Delete
const deleteResult = await fetch(`${baseUrl}/users/123`, {
  method: 'DELETE',
  headers: { 'Authorization': `Bearer ${apiKey}` }
});

Types

/**
 * Request URL or Request object
 */
type RequestInfo = string | URL | Request;

/**
 * Request configuration options
 */
interface RequestInit {
  method?: string;
  headers?: HeadersInit;
  body?: BodyInit | null;
  mode?: RequestMode;
  credentials?: RequestCredentials;
  cache?: RequestCache;
  redirect?: RequestRedirect;
  referrer?: string;
  referrerPolicy?: ReferrerPolicy;
  integrity?: string;
  keepalive?: boolean;
  signal?: AbortSignal | null;
}

/**
 * HTTP response object
 */
interface Response extends Body {
  readonly headers: Headers;
  readonly ok: boolean;
  readonly redirected: boolean;
  readonly status: number;
  readonly statusText: string;
  readonly type: ResponseType;
  readonly url: string;
  
  clone(): Response;
}

/**
 * Response body methods
 */
interface Body {
  readonly body: ReadableStream<Uint8Array> | null;
  readonly bodyUsed: boolean;
  
  arrayBuffer(): Promise<ArrayBuffer>;
  blob(): Promise<Blob>;
  formData(): Promise<FormData>;
  json(): Promise<any>;
  text(): Promise<string>;
}

Install with Tessl CLI

npx tessl i tessl/npm-zx

docs

cli.md

file-system.md

index.md

network-operations.md

process-management.md

shell-execution.md

user-interaction.md

utilities.md

tile.json