or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

connection-management.mdcookies.mderror-handling.mdhttp-api.mdhttp-clients.mdindex.mdinterceptors.mdtesting-mocking.mdutilities.mdweb-standards.md

web-standards.mddocs/

0

# Web Standards APIs

1

2

Web-compatible implementations of fetch, WebSocket, FormData and related APIs that follow standard web specifications. These APIs provide familiar interfaces for developers coming from browser environments.

3

4

## Capabilities

5

6

### Fetch API

7

8

Standards-compliant fetch implementation for HTTP requests.

9

10

```typescript { .api }

11

/**

12

* Perform HTTP requests using the standard web fetch API

13

* @param input - URL string or Request object

14

* @param init - Request configuration options

15

* @returns Promise resolving to Response object

16

*/

17

function fetch(

18

input: RequestInfo,

19

init?: RequestInit

20

): Promise<Response>;

21

22

type RequestInfo = Request | string | URL;

23

24

interface RequestInit {

25

method?: string;

26

headers?: HeadersInit;

27

body?: BodyInit;

28

mode?: RequestMode;

29

credentials?: RequestCredentials;

30

cache?: RequestCache;

31

redirect?: RequestRedirect;

32

referrer?: string;

33

referrerPolicy?: ReferrerPolicy;

34

integrity?: string;

35

keepalive?: boolean;

36

signal?: AbortSignal;

37

window?: null;

38

duplex?: RequestDuplex;

39

}

40

41

type RequestMode = "cors" | "no-cors" | "same-origin" | "navigate";

42

type RequestCredentials = "omit" | "same-origin" | "include";

43

type RequestCache = "default" | "no-store" | "reload" | "no-cache" | "force-cache" | "only-if-cached";

44

type RequestRedirect = "follow" | "error" | "manual";

45

type RequestDuplex = "half";

46

```

47

48

**Usage Examples:**

49

50

```typescript

51

import { fetch } from "undici-types";

52

53

// Simple GET request

54

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

55

const users = await response.json();

56

57

// POST request with JSON

58

const response = await fetch("https://api.example.com/users", {

59

method: "POST",

60

headers: {

61

"Content-Type": "application/json"

62

},

63

body: JSON.stringify({

64

name: "John Doe",

65

email: "john@example.com"

66

})

67

});

68

69

// Request with AbortController

70

const controller = new AbortController();

71

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

72

73

try {

74

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

75

signal: controller.signal

76

});

77

} catch (error) {

78

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

79

console.log('Request was aborted');

80

}

81

}

82

```

83

84

### Request Class

85

86

Represents an HTTP request with full configuration options.

87

88

```typescript { .api }

89

/**

90

* Represents an HTTP request

91

*/

92

class Request implements BodyMixin {

93

constructor(input: RequestInfo, init?: RequestInit);

94

95

readonly method: string;

96

readonly url: string;

97

readonly headers: Headers;

98

readonly body: ReadableStream<Uint8Array> | null;

99

readonly bodyUsed: boolean;

100

readonly cache: RequestCache;

101

readonly credentials: RequestCredentials;

102

readonly destination: RequestDestination;

103

readonly integrity: string;

104

readonly keepalive: boolean;

105

readonly mode: RequestMode;

106

readonly redirect: RequestRedirect;

107

readonly referrer: string;

108

readonly referrerPolicy: ReferrerPolicy;

109

readonly signal: AbortSignal;

110

111

clone(): Request;

112

113

// BodyMixin methods

114

arrayBuffer(): Promise<ArrayBuffer>;

115

blob(): Promise<Blob>;

116

formData(): Promise<FormData>;

117

json(): Promise<any>;

118

text(): Promise<string>;

119

}

120

121

type RequestDestination = "" | "audio" | "audioworklet" | "document" | "embed" | "font" | "frame" | "iframe" | "image" | "manifest" | "object" | "paintworklet" | "report" | "script" | "serviceworker" | "sharedworker" | "style" | "track" | "video" | "worker" | "xslt";

122

```

123

124

**Usage Examples:**

125

126

```typescript

127

import { Request, fetch } from "undici-types";

128

129

// Create reusable request

130

const apiRequest = new Request("https://api.example.com/data", {

131

method: "POST",

132

headers: {

133

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

134

"Authorization": "Bearer token123"

135

},

136

body: JSON.stringify({ query: "users" })

137

});

138

139

// Use request multiple times

140

const response1 = await fetch(apiRequest.clone());

141

const response2 = await fetch(apiRequest.clone());

142

143

// Access request properties

144

console.log(apiRequest.method); // "POST"

145

console.log(apiRequest.url); // "https://api.example.com/data"

146

console.log(apiRequest.headers.get("Content-Type")); // "application/json"

147

```

148

149

### Response Class

150

151

Represents an HTTP response with body parsing methods.

152

153

```typescript { .api }

154

/**

155

* Represents an HTTP response

156

*/

157

class Response implements BodyMixin {

158

constructor(body?: BodyInit | null, init?: ResponseInit);

159

160

readonly type: ResponseType;

161

readonly url: string;

162

readonly redirected: boolean;

163

readonly status: number;

164

readonly ok: boolean;

165

readonly statusText: string;

166

readonly headers: Headers;

167

readonly body: ReadableStream<Uint8Array> | null;

168

readonly bodyUsed: boolean;

169

170

clone(): Response;

171

172

// BodyMixin methods

173

arrayBuffer(): Promise<ArrayBuffer>;

174

blob(): Promise<Blob>;

175

formData(): Promise<FormData>;

176

json(): Promise<any>;

177

text(): Promise<string>;

178

179

// Static methods

180

static error(): Response;

181

static json(data: any, init?: ResponseInit): Response;

182

static redirect(url: string | URL, status?: ResponseRedirectStatus): Response;

183

}

184

185

interface ResponseInit {

186

status?: number;

187

statusText?: string;

188

headers?: HeadersInit;

189

}

190

191

type ResponseType = "basic" | "cors" | "default" | "error" | "opaque" | "opaqueredirect";

192

type ResponseRedirectStatus = 300 | 301 | 302 | 303 | 307 | 308;

193

```

194

195

**Usage Examples:**

196

197

```typescript

198

import { fetch, Response } from "undici-types";

199

200

// Handle response

201

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

202

203

if (response.ok) {

204

const contentType = response.headers.get("content-type");

205

206

if (contentType?.includes("application/json")) {

207

const data = await response.json();

208

console.log(data);

209

} else {

210

const text = await response.text();

211

console.log(text);

212

}

213

} else {

214

console.error(`Error: ${response.status} ${response.statusText}`);

215

}

216

217

// Create custom responses

218

const jsonResponse = Response.json({ message: "Hello" }, {

219

status: 200,

220

headers: { "Custom-Header": "value" }

221

});

222

223

const redirectResponse = Response.redirect("https://example.com/new-location", 301);

224

```

225

226

### Headers Class

227

228

HTTP headers manipulation following web standards.

229

230

```typescript { .api }

231

/**

232

* HTTP headers collection with case-insensitive access

233

*/

234

class Headers {

235

constructor(init?: HeadersInit);

236

237

append(name: string, value: string): void;

238

delete(name: string): void;

239

get(name: string): string | null;

240

getSetCookie(): string[];

241

has(name: string): boolean;

242

set(name: string, value: string): void;

243

244

forEach(callback: (value: string, key: string, parent: Headers) => void, thisArg?: any): void;

245

keys(): IterableIterator<string>;

246

values(): IterableIterator<string>;

247

entries(): IterableIterator<[string, string]>;

248

[Symbol.iterator](): IterableIterator<[string, string]>;

249

}

250

251

type HeadersInit = Headers | Record<string, string | string[]> | Iterable<readonly [string, string]>;

252

```

253

254

**Usage Examples:**

255

256

```typescript

257

import { Headers, fetch } from "undici-types";

258

259

// Create headers object

260

const headers = new Headers();

261

headers.set("Content-Type", "application/json");

262

headers.set("Authorization", "Bearer token123");

263

headers.append("Accept", "application/json");

264

265

// Use with fetch

266

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

267

method: "POST",

268

headers: headers,

269

body: JSON.stringify({ data: "example" })

270

});

271

272

// Iterate headers

273

for (const [name, value] of headers) {

274

console.log(`${name}: ${value}`);

275

}

276

277

// Create from object

278

const headers2 = new Headers({

279

"Content-Type": "text/plain",

280

"Custom-Header": "custom-value"

281

});

282

283

// Create from array

284

const headers3 = new Headers([

285

["Content-Type", "application/xml"],

286

["Accept", "application/xml"]

287

]);

288

```

289

290

### WebSocket

291

292

WebSocket client implementation following web standards.

293

294

```typescript { .api }

295

/**

296

* WebSocket client implementation

297

*/

298

class WebSocket extends EventTarget {

299

constructor(url: string | URL, protocols?: string | string[], options?: WebSocketInit);

300

301

static readonly CONNECTING: 0;

302

static readonly OPEN: 1;

303

static readonly CLOSING: 2;

304

static readonly CLOSED: 3;

305

306

readonly CONNECTING: 0;

307

readonly OPEN: 1;

308

readonly CLOSING: 2;

309

readonly CLOSED: 3;

310

311

readonly url: string;

312

readonly readyState: number;

313

readonly extensions: string;

314

readonly protocol: string;

315

316

binaryType: BinaryType;

317

318

onopen: ((this: WebSocket, ev: Event) => any) | null;

319

onmessage: ((this: WebSocket, ev: MessageEvent) => any) | null;

320

onerror: ((this: WebSocket, ev: ErrorEvent) => any) | null;

321

onclose: ((this: WebSocket, ev: CloseEvent) => any) | null;

322

323

send(data: string | ArrayBuffer | ArrayBufferView | Blob): void;

324

close(code?: number, reason?: string): void;

325

326

addEventListener<K extends keyof WebSocketEventMap>(

327

type: K,

328

listener: (this: WebSocket, ev: WebSocketEventMap[K]) => any,

329

options?: boolean | AddEventListenerOptions

330

): void;

331

332

removeEventListener<K extends keyof WebSocketEventMap>(

333

type: K,

334

listener: (this: WebSocket, ev: WebSocketEventMap[K]) => any,

335

options?: boolean | EventListenerOptions

336

): void;

337

}

338

339

interface WebSocketInit {

340

protocols?: string | string[];

341

dispatcher?: Dispatcher;

342

headers?: HeadersInit;

343

followRedirects?: boolean;

344

}

345

346

type BinaryType = "blob" | "arraybuffer";

347

348

interface WebSocketEventMap {

349

"close": CloseEvent;

350

"error": ErrorEvent;

351

"message": MessageEvent;

352

"open": Event;

353

}

354

355

interface CloseEvent extends Event {

356

readonly code: number;

357

readonly reason: string;

358

readonly wasClean: boolean;

359

}

360

361

interface MessageEvent extends Event {

362

readonly data: any;

363

readonly lastEventId: string;

364

readonly origin: string;

365

readonly ports: readonly MessagePort[];

366

readonly source: MessageEventSource | null;

367

}

368

369

interface ErrorEvent extends Event {

370

readonly message: string;

371

readonly filename: string;

372

readonly lineno: number;

373

readonly colno: number;

374

readonly error: any;

375

}

376

```

377

378

**Usage Examples:**

379

380

```typescript

381

import { WebSocket } from "undici-types";

382

383

// Basic WebSocket connection

384

const ws = new WebSocket("ws://localhost:8080");

385

386

ws.onopen = (event) => {

387

console.log("WebSocket connected");

388

ws.send("Hello Server!");

389

};

390

391

ws.onmessage = (event) => {

392

console.log("Received:", event.data);

393

};

394

395

ws.onclose = (event) => {

396

console.log(`WebSocket closed: ${event.code} ${event.reason}`);

397

};

398

399

ws.onerror = (event) => {

400

console.error("WebSocket error:", event.error);

401

};

402

403

// WebSocket with protocols and headers

404

const ws2 = new WebSocket("wss://api.example.com/ws", ["protocol1", "protocol2"], {

405

headers: {

406

"Authorization": "Bearer token123"

407

}

408

});

409

410

// Send different data types

411

ws2.onopen = () => {

412

ws2.send("text message");

413

ws2.send(new ArrayBuffer(8));

414

ws2.send(new Uint8Array([1, 2, 3, 4]));

415

};

416

417

// Handle binary data

418

ws2.binaryType = "arraybuffer";

419

ws2.onmessage = (event) => {

420

if (event.data instanceof ArrayBuffer) {

421

console.log("Received binary data:", new Uint8Array(event.data));

422

} else {

423

console.log("Received text:", event.data);

424

}

425

};

426

```

427

428

### WebSocket Utilities

429

430

Additional WebSocket utilities for advanced operations.

431

432

```typescript { .api }

433

/**

434

* Send a ping frame to a WebSocket

435

* @param ws - The WebSocket instance

436

* @param data - Optional ping payload

437

* @returns Promise that resolves when ping is sent

438

*/

439

function ping(ws: WebSocket, data?: Buffer | ArrayBuffer | ArrayBufferView): Promise<void>;

440

441

/**

442

* WebSocket stream interface for streaming operations

443

*/

444

class WebSocketStream {

445

constructor(url: string | URL, options?: WebSocketStreamOptions);

446

447

readonly url: string;

448

readonly readyState: number;

449

readonly extensions: string;

450

readonly protocol: string;

451

452

readonly readable: ReadableStream;

453

readonly writable: WritableStream;

454

455

close(closeInfo?: WebSocketCloseInfo): Promise<void>;

456

}

457

458

interface WebSocketStreamOptions {

459

protocols?: string | string[];

460

signal?: AbortSignal;

461

}

462

463

interface WebSocketCloseInfo {

464

code?: number;

465

reason?: string;

466

}

467

```

468

469

**Usage Examples:**

470

471

```typescript

472

import { WebSocket, WebSocketStream, ping } from "undici-types";

473

474

// Ping a WebSocket

475

const ws = new WebSocket("ws://localhost:8080");

476

ws.onopen = async () => {

477

await ping(ws, Buffer.from("ping-data"));

478

console.log("Ping sent");

479

};

480

481

// Use WebSocket streams

482

const wsStream = new WebSocketStream("ws://localhost:8080/stream");

483

484

const writer = wsStream.writable.getWriter();

485

await writer.write("Hello from stream");

486

487

const reader = wsStream.readable.getReader();

488

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

489

if (!done) {

490

console.log("Received:", value);

491

}

492

493

// Close stream

494

await wsStream.close({ code: 1000, reason: "Done" });

495

```

496

497

### FormData

498

499

Form data construction and parsing for multipart/form-data.

500

501

```typescript { .api }

502

/**

503

* Represents form data for multipart/form-data encoding

504

*/

505

class FormData {

506

append(name: string, value: string | Blob, fileName?: string): void;

507

delete(name: string): void;

508

get(name: string): FormDataEntryValue | null;

509

getAll(name: string): FormDataEntryValue[];

510

has(name: string): boolean;

511

set(name: string, value: string | Blob, fileName?: string): void;

512

513

forEach(

514

callback: (value: FormDataEntryValue, key: string, parent: FormData) => void,

515

thisArg?: any

516

): void;

517

518

keys(): IterableIterator<string>;

519

values(): IterableIterator<FormDataEntryValue>;

520

entries(): IterableIterator<[string, FormDataEntryValue]>;

521

[Symbol.iterator](): IterableIterator<[string, FormDataEntryValue]>;

522

}

523

524

type FormDataEntryValue = File | string;

525

526

interface File extends Blob {

527

readonly lastModified: number;

528

readonly name: string;

529

readonly webkitRelativePath: string;

530

}

531

532

interface Blob {

533

readonly size: number;

534

readonly type: string;

535

536

arrayBuffer(): Promise<ArrayBuffer>;

537

slice(start?: number, end?: number, contentType?: string): Blob;

538

stream(): ReadableStream<Uint8Array>;

539

text(): Promise<string>;

540

}

541

```

542

543

**Usage Examples:**

544

545

```typescript

546

import { FormData, fetch } from "undici-types";

547

548

// Create form data

549

const formData = new FormData();

550

formData.append("username", "john_doe");

551

formData.append("email", "john@example.com");

552

formData.append("avatar", new Blob(["image data"], { type: "image/jpeg" }), "avatar.jpg");

553

554

// Send form data

555

const response = await fetch("https://api.example.com/upload", {

556

method: "POST",

557

body: formData

558

});

559

560

// Iterate form data

561

for (const [key, value] of formData) {

562

console.log(`${key}:`, value);

563

}

564

565

// Check and manipulate form data

566

if (formData.has("username")) {

567

console.log("Username:", formData.get("username"));

568

}

569

570

formData.set("username", "jane_doe"); // Replace existing value

571

formData.delete("email"); // Remove field

572

```

573

574

### EventSource

575

576

Server-Sent Events (SSE) client implementation for receiving real-time updates from servers.

577

578

```typescript { .api }

579

/**

580

* EventSource client for Server-Sent Events (SSE)

581

* Maintains a persistent connection to receive server events

582

*/

583

class EventSource extends EventTarget {

584

constructor(url: string | URL, init?: EventSourceInit);

585

586

static readonly CLOSED: 2;

587

static readonly CONNECTING: 0;

588

static readonly OPEN: 1;

589

590

readonly CLOSED: 2;

591

readonly CONNECTING: 0;

592

readonly OPEN: 1;

593

594

/** Current connection state */

595

readonly readyState: 0 | 1 | 2;

596

597

/** Event source URL */

598

readonly url: string;

599

600

/** Whether credentials are included in requests */

601

readonly withCredentials: boolean;

602

603

/** Event handler for connection errors */

604

onerror: ((this: EventSource, ev: ErrorEvent) => any) | null;

605

606

/** Event handler for received messages */

607

onmessage: ((this: EventSource, ev: MessageEvent) => any) | null;

608

609

/** Event handler for connection open */

610

onopen: ((this: EventSource, ev: Event) => any) | null;

611

612

/** Close the connection */

613

close(): void;

614

615

addEventListener<K extends keyof EventSourceEventMap>(

616

type: K,

617

listener: (this: EventSource, ev: EventSourceEventMap[K]) => any,

618

options?: boolean | AddEventListenerOptions

619

): void;

620

621

removeEventListener<K extends keyof EventSourceEventMap>(

622

type: K,

623

listener: (this: EventSource, ev: EventSourceEventMap[K]) => any,

624

options?: boolean | EventListenerOptions

625

): void;

626

}

627

628

interface EventSourceInit {

629

/** Include credentials with requests */

630

withCredentials?: boolean;

631

632

/** Custom dispatcher for the connection */

633

dispatcher?: Dispatcher;

634

635

/** Node.js specific options */

636

node?: {

637

/** Custom dispatcher for Node.js */

638

dispatcher?: Dispatcher;

639

640

/** Reconnection delay in milliseconds */

641

reconnectionTime?: number;

642

};

643

}

644

645

interface EventSourceEventMap {

646

/** Connection error event */

647

error: ErrorEvent;

648

649

/** Message received event */

650

message: MessageEvent;

651

652

/** Connection opened event */

653

open: Event;

654

}

655

```

656

657

**Usage Examples:**

658

659

```typescript

660

import { EventSource } from "undici-types";

661

662

// Basic EventSource connection

663

const eventSource = new EventSource("https://api.example.com/events");

664

665

// Handle connection events

666

eventSource.onopen = (event) => {

667

console.log("EventSource connection opened");

668

console.log("Ready state:", eventSource.readyState); // 1 (OPEN)

669

};

670

671

eventSource.onmessage = (event) => {

672

console.log("Received message:", event.data);

673

674

// Parse JSON data if expected

675

try {

676

const data = JSON.parse(event.data);

677

console.log("Parsed data:", data);

678

} catch (e) {

679

console.log("Plain text message:", event.data);

680

}

681

};

682

683

eventSource.onerror = (event) => {

684

console.error("EventSource error:", event);

685

686

if (eventSource.readyState === EventSource.CLOSED) {

687

console.log("Connection closed");

688

} else {

689

console.log("Connection error, will retry...");

690

}

691

};

692

693

// EventSource with credentials and custom options

694

const authEventSource = new EventSource("https://api.example.com/private-events", {

695

withCredentials: true,

696

node: {

697

reconnectionTime: 5000 // 5 second reconnection delay

698

}

699

});

700

701

// Listen for custom event types

702

authEventSource.addEventListener("user-update", (event) => {

703

console.log("User update received:", event.data);

704

});

705

706

authEventSource.addEventListener("notification", (event) => {

707

const notification = JSON.parse(event.data);

708

showNotification(notification.title, notification.message);

709

});

710

711

// Graceful shutdown

712

window.addEventListener("beforeunload", () => {

713

eventSource.close();

714

authEventSource.close();

715

});

716

717

// Manual connection management

718

function connectWithRetry(url: string, maxRetries = 3) {

719

let retryCount = 0;

720

721

function connect() {

722

const es = new EventSource(url);

723

724

es.onerror = (event) => {

725

if (es.readyState === EventSource.CLOSED && retryCount < maxRetries) {

726

console.log(`Retrying connection... (${retryCount + 1}/${maxRetries})`);

727

retryCount++;

728

setTimeout(connect, 1000 * Math.pow(2, retryCount)); // Exponential backoff

729

} else if (retryCount >= maxRetries) {

730

console.error("Max retries reached, giving up");

731

}

732

};

733

734

es.onopen = () => {

735

retryCount = 0; // Reset retry count on successful connection

736

console.log("Connected successfully");

737

};

738

739

return es;

740

}

741

742

return connect();

743

}

744

745

const resilientEventSource = connectWithRetry("https://api.example.com/stream");

746

```

747

748

### BodyMixin Interface

749

750

Common interface providing body parsing methods for Request and Response classes.

751

752

```typescript { .api }

753

/**

754

* Common body parsing methods for Request and Response

755

*/

756

interface BodyMixin {

757

/** Raw body stream */

758

readonly body: ReadableStream<Uint8Array> | null;

759

760

/** Whether the body has been consumed */

761

readonly bodyUsed: boolean;

762

763

/** Parse body as ArrayBuffer */

764

arrayBuffer(): Promise<ArrayBuffer>;

765

766

/** Parse body as Blob */

767

blob(): Promise<Blob>;

768

769

/** Parse body as Uint8Array */

770

bytes(): Promise<Uint8Array>;

771

772

/** Parse body as FormData (deprecated for server parsing) */

773

formData(): Promise<FormData>;

774

775

/** Parse body as JSON */

776

json(): Promise<unknown>;

777

778

/** Parse body as text string */

779

text(): Promise<string>;

780

}

781

782

type BodyInit =

783

| ArrayBuffer

784

| AsyncIterable<Uint8Array>

785

| Blob

786

| FormData

787

| Iterable<Uint8Array>

788

| NodeJS.ArrayBufferView

789

| URLSearchParams

790

| null

791

| string;

792

```