-
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathHttp.affine
More file actions
117 lines (103 loc) · 4.86 KB
/
Copy pathHttp.affine
File metadata and controls
117 lines (103 loc) · 4.86 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
// SPDX-License-Identifier: PMPL-1.0-or-later
// SPDX-FileCopyrightText: 2026 hyperpolymath
//
// Http.affine — portable HTTP requests (issue #160).
//
// The C-spine migration primitive that replaces ReScript's
// `fetch`/`Promise`-based networking with a typed, effect-rowed surface.
//
// Portability: the single `http_request` extern is lowered by the
// Deno-ESM backend (lib/codegen_deno.ml) to a `globalThis.fetch`
// round-trip, which runs unmodified on Deno, Node 18+, and browser ESM.
// The WasmGC / Node-CJS host-import path is a tracked follow-up
// (the runtime there has no `fetch` import yet) — see the issue thread.
//
// Headers are an association list `[(String, String)]` rather than a
// `Dict<String, String>`: this lets #160 land independently of #162
// (Dict) while keeping the stated spine order #160 -> #161 -> #162.
// Upgrading `headers` to `Dict` later is an additive, source-compatible
// change for callers that build/read headers through the helpers here.
module Http;
// `Option`/`Some`/`None` are owned by `prelude` (ADR-011).
use prelude::{Option, Some, None};
// Networking uses the reserved built-in effect `Net` (no declaration
// needed — it is one of the language's reserved effect names alongside
// `Random`/`Time`). Every signature that can touch the network carries
// `/ Net`; the host round-trip is also `Async`, so the effect row
// stays honest end to end. A bespoke `effect Http;` would not be
// visible to importing modules' effect checker, so `Net` is both more
// correct and the only cross-module-sound choice.
/// An outgoing HTTP request.
///
/// `headers` is an ordered association list of `(name, value)` pairs.
/// `body` is absent for verbs that take none (GET/HEAD).
pub type Request = {
url: String,
method: String,
headers: [(String, String)],
body: Option<String>
}
/// A host HTTP response. `body` is the raw response text; structured
/// decoding (JSON) is the caller's concern and is the subject of the
/// next spine primitive, #161.
pub type Response = {
status: Int,
headers: [(String, String)],
body: String
}
// Core boundary primitive. No AffineScript body: the Deno-ESM backend
// lowers a call here to `(await __as_httpFetch(url, method, headers,
// body))`. The `Async` effect is real — callers compile to
// `async function` (codegen_deno async-fn detection).
extern fn http_request(url: String,
method: String,
headers: [(String, String)],
body: Option<String>) -> Response / { Net, Async };
/// Build a `Request` value (ergonomic constructor for the record).
pub fn request(method: String,
url: String,
headers: [(String, String)],
body: Option<String>) -> Request {
#{ url: url, method: method, headers: headers, body: body }
}
/// Perform the HTTP request described by `req`.
pub fn fetch(req: Request) -> Response / { Net, Async } {
http_request(req.url, req.method, req.headers, req.body)
}
/// `GET url` with no extra request headers.
pub fn get(url: String) -> Response / { Net, Async } {
http_request(url, "GET", [], None)
}
/// `POST body` to `url` with no extra request headers.
pub fn post(url: String, body: String) -> Response / { Net, Async } {
http_request(url, "POST", [], Some(body))
}
/// True for a 2xx status.
pub fn is_ok(resp: Response) -> Bool {
resp.status >= 200 && resp.status < 300
}
// ── Lower-level wasm-consumable surface (issue #225, ADR-013) ─────────
//
// On the WasmGC backend an async result cannot be returned synchronously
// across the i32 boundary. The established convergence convention (#205)
// is a `Thenable` host handle the side resolving it observes via the
// shared continuation protocol. `http_request_thenable` mirrors the
// proven `Vscode.httpPostJson` shape exactly: it returns a `Thenable`
// handle the host registers; settlement carries the JSON-encoded
// `{ status, headers, body }`.
//
// This is NOT a second public surface: `fetch`/`get`/`post` above remain
// the single portable source API. On Deno the source-to-source backend
// awaits directly and never touches this. On wasm the transparent CPS
// transform (ADR-013) lowers the high-level surface ONTO this verified
// primitive — callers still write `fetch(req) -> Response`. This
// declaration is the host-import contract + the foundation that
// transform is built and tested on (#225 PR 1).
pub extern type Thenable;
/// Issue an HTTP request, returning a host `Thenable` that settles with
/// the JSON-encoded response `{ status, headers, body }`. `body` is the
/// empty string for verbs that carry none. Wasm-path primitive; see the
/// module note. Resolved via the shared #205 continuation protocol.
pub extern fn http_request_thenable(url: String,
method: String,
body: String) -> Thenable / { Net, Async };