Skip to main content

Phase 14. fetch (mochiHttpGet)

FieldValue
MEPMEP-49 §Phases · Phase 14
StatusLANDED
Started2026-05-28 13:40 (GMT+7)
Landed2026-05-28 13:40 (GMT+7)
Tracking issue
Tracking PR

Gate

TestPhase14Fetch: 5 fixtures green on Swift 6.0+, macOS 15. Gate builds each fixture and compares stdout to .expected. Fixtures use a local file-scheme URL or a server started in the test.

Goal-alignment audit

The v1 fetch gate uses synchronous Data(contentsOf: url) which works for the fixture suite and requires no async colouring. The full URLSession.data(for:) async path, SSE streaming, WebSocket, and mock URLProtocol are deferred. This keeps Phase 14 simple while unblocking fixtures that need to read remote (or local file-scheme) content.

Sub-phases

#ScopeStatusCommit
14.0FetchExprmochiHttpGet(url)LANDEDmep/0049-phase-14
14.1URLSession.data(for:) async; JSON decode via CodableDEFERRED
14.2Streaming responses via URLSession.bytes(for:) + SSE parsingDEFERRED
14.3WebSocket via URLSessionWebSocketTaskDEFERRED
14.4MockURLProtocol for in-process HTTP mockingDEFERRED

Sub-phase 14.0 -- HTTP GET

Decisions made (14.0)

mochiHttpGet(_ urlString: String) -> String: synchronous fetch via Data(contentsOf:). Returns the response body as a UTF-8 string, trimmed of trailing newlines. Returns "" on invalid URL or network error.

public func mochiHttpGet(_ urlString: String) -> String {
guard let url = URL(string: urlString) else { return "" }
guard let data = try? Data(contentsOf: url) else { return "" }
return (String(data: data, encoding: .utf8) ?? "").trimmingCharacters(in: .newlines)
}

Lowering: FetchExpr in the aotir IR lowers to mochiHttpGet(urlExpr) as a RawSwiftExpr. The same pattern is also handled in the C backend's lowerFetchExpr.

Synchronous: Data(contentsOf:) blocks the calling thread. This is acceptable for the fixture suite and CLI programs; async URLSession is deferred.

C backend parity: transpiler3/c/lower/lower.go was updated in the same phase to add lowerFetchExpr for parser.FetchExpr and the pr.Fetch != nil branch in lowerPrimary, ensuring the fetch lowering pattern is consistent across backends.

Files changed

FilePurpose
transpiler3/swift/lower/lower.goFetchExpr lowering to mochiHttpGet(url)
transpiler3/c/lower/lower.golowerFetchExpr; pr.Fetch != nil branch in lowerPrimary
transpiler3/swift/runtime/Sources/MochiRuntime/Fetch.swiftmochiHttpGet
transpiler3/swift/build/phase14_test.goTestPhase14Fetch: 5 fixtures
tests/transpiler3/swift/fixtures/phase14-fetch/5 fixture directories

Test set

  • TestPhase14Fetch -- 5 fixtures: fetch_basic, fetch_json_string, fetch_multiline, fetch_string, fetch_use_result.

Deferred work

  • URLSession.data(for:) async HTTP; MochiFetchError typed error enum. Deferred to Phase 14.1.
  • Streaming responses via URLSession.bytes(for:) + SSE data: line parsing. Deferred to Phase 14.2.
  • WebSocket via URLSessionWebSocketTask; webSocketStream(url:) helper. Deferred to Phase 14.3.
  • MockURLProtocol for in-process HTTP mock (no external server needed in CI). Deferred to Phase 14.4.
  • POST with body; custom headers. Deferred to Phase 14.1.