LeCodesdocs

Networking

HTTP and sockets over the host bridge. They read like the web APIs but are not them — the one thing to internalize: after await fetch(...) resolves, the body is already on the host, so res.json() / res.text() are synchronous — no second await.

At a glance

TypeScript
const res = await fetch('https://api.example.com/items')
if (res.status === 200) {
  const items = res.json<{ id: number, name: string }[]>()   // synchronous — no await
  console.log(items.length)
}
res.dispose()

fetch

TypeScript
fetch(url: string, options?: {
  method?: string                        // 'GET' (default), 'POST', …
  headers?: Record<string, string>
  body?: any                             // string, or a FormData
  useOnce?: boolean                      // one-shot body — host may release it after the read
  onProgress?: (p: { loaded: number, total?: number }) => void   // download progress, bytes
}): Promise<FetchResponse>

The promise resolves once the host has the whole response — status and body. An HTTP error status (404, 500) still resolves; check res.status. Rejection means the request itself failed (no connection, bad URL).

onProgress reports downloaded bytes; total is absent when the server sends no length. Pass useOnce: true for fire-and-forget requests whose body you read exactly once — the SDK's own loaders (Texture2D.load, Model.load) do.

FetchResponse

TypeScript
res.status          // HTTP status code (read-only)
res.json<T>(): T    // parse body as JSON — SYNCHRONOUS
res.text(): string  // body as text — SYNCHRONOUS
res.dispose()       // release the host-side body buffer

The body lives in host-side storage; json()/text() pull it across. A FetchResponse is also accepted directly as an image source (UIImage(res)) and a FormData value.

Note

json()/text() are plain synchronous calls. await res.json() "works" (awaiting a plain value) but signals the wrong mental model — don't write it.

Note

ownership — the host holds the body until you dispose() (or you fetched with useOnce: true). Dispose responses you keep around once you're done with them.

fetchLocal

TypeScript
fetchLocal(path: string): FetchResponse    // synchronous, status always 200

Read a bundled project resource — no promise, no network. Use it with the asset() macro for data files you ship:

TypeScript
const config = fetchLocal(asset('./config.json')).json<{ levels: number }>()

File

TypeScript
file.id      // host-side buffer handle (read-only)
file.name    // file name (read-only)
file.size    // bytes (read-only)

A handle to a picked or captured file. You don't construct these — they come from openFilePicker and CameraViewer.takePhoto(). Append to a FormData to upload, use as an image source, or share() it.

FormData

TypeScript
const form = new FormData()
form.append(name: string, value: string | number | boolean | File | FetchResponse,
            filename?: string)     // filename defaults to a File's own name
form.delete(name: string)          // removes the first field with that name

Build a multipart body, then pass it as fetch's body. This is a minimal subset — no get, has, set, or iteration.

TypeScript
const file = await openFilePicker({ accept: 'image/*' })
if (file) {
  const form = new FormData()
  form.append('avatar', file)
  form.append('userId', 123)
  await fetch('https://api.example.com/upload', { method: 'POST', body: form })
}

WebSocket

TypeScript
const ws = new WebSocket(url: string, headers?: Record<string, string>)  // connects immediately
ws.send(message: string | ArrayBuffer)
ws.close()

Browser-like event surface, with one non-standard extra: the optional headers argument goes on the upgrade request (handy for auth tokens). Listen with addEventListener / removeEventListener (the shared emitter pattern — see Conventions):

TypeScript
ws.addEventListener('open',    () => ws.send('hello'))
ws.addEventListener('message', data => console.log('got', data))   // text or binary frame
ws.addEventListener('close',   code => console.log('closed', code))
ws.addEventListener('error',   () => console.log('socket error'))

After close() the socket is inert — further send() calls are silent no-ops. Close sockets in a screen's onClose, or they keep running across navigation.

Pitfalls

TypeScript
// ✗ web-fetch habits — body readers are not async here
const data = await (await fetch(url)).json()
// ✓ await the fetch once, then read synchronously
const res = await fetch(url); const data = res.json()

// ✗ treating a 404 as a rejection
try { await fetch(url) } catch { /* expecting 404 here — it won't arrive */ }
// ✓ HTTP statuses resolve; check status yourself
const res = await fetch(url); if (res.status !== 200) handleError(res.status)

See also