You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Commit c005a91
Browse filesBrowse the repository at this point in the historyBrowse files
Closes#363
## Summary
Add an opt-in `WebAPI.SafeFetch` module that converts expected Fetch,
HTTP-status, and response-body failures into composable `result` values.
Raw `WebAPI.Fetch` and `WebAPI.Response` bindings remain unchanged.
- Select the separate `WebAPI.SafeFetch` feature; it enables
`WebAPI.Fetch` and the shared raw types transitively.
- Use tagged polymorphic errors consistently: `#FetchRejected(cause)`,
`#ResponseNotOk({response})`, and `#BodyReadRejected({response,
cause})`.
- Public signatures expose extensible error rows, so `let?` can compose
fetch, body reading, and application decoding without `mapError`
adapters.
- Keep the original response and rejection cause. Successful body reads
retain `{response, body}`; the body has been consumed.
- Cover all six body readers. `json` returns `JSON.t`;
application-specific validation stays with the caller.
This intentionally changes the experimental API: consumers that
previously selected only `WebAPI.Fetch` must explicitly select
`WebAPI.SafeFetch` to use the wrapper.
## Usage
The locked ReScript 13 alpha compiler requires `LetUnwrap` for `let?`;
ordinary `switch` usage does not require that setting.
```json
{
"experimental-features": {"LetUnwrap": true},
"dependencies": [
{"name": "@rescript/webapi", "features": ["WebAPI.SafeFetch"]}
]
}
```
```ReScript
type user = {name: string}
let decodeUser = json =>
switch json {
| JSON.Object(dict{"name": JSON.String(name)}) => Ok({name: name})
| _ => Error(#InvalidUser)
}
let loadUser = async url => {
let? Ok(response) = await WebAPI.SafeFetch.fetch(url)
let? Ok({body}) = await response->WebAPI.SafeFetch.json
let? Ok(user) = body->decodeUser
Ok(user)
}
```
The caller handles four distinct error tags in one switch: request
rejection, non-OK HTTP status, body-read rejection, and
application-owned `#InvalidUser`. Malformed JSON is a body-read error;
valid JSON with the wrong shape is an application-decoding error. A
non-OK response remains available for reading error details.
## Review follow-up
- Converted all error stages to polymorphic variants, including
`checkOk` and body readers.
- Removed redundant implementation annotations; retained the `checkOk`
response annotation required for record-field inference.
- Moved the wrapper to `src/safe-fetch` with its own feature and added
isolated public-consumer checks.
- Added documented and compiled `let?` examples plus runtime
propagation/short-circuit tests.
Both fetch entry points still construct their final result directly and
call native `fetch`. The late public alias prevents native-fetch
shadowing, optional initializers are forwarded or omitted, and the only
generated runtime import is exception normalization. `exn` preserves
arbitrary JavaScript abort reasons via `JsExn.fromException`.
## Validation
Passed locally with Node 26.3.0 and locked ReScript 13.0.0-alpha.5:
- `npm ci`
- `npm run format:check`
- `npm run build`
- `npm run check:features` — four checker unit tests, all 28 clean
public feature builds, a SafeFetch-only consumer with composed
application errors, a raw Fetch-only consumer, and a negative check
proving SafeFetch is unavailable with only Fetch enabled
- `npm test` — all seven runtime suites, including native default/custom
abort reasons, both fetch entry points, optional initialization,
synchronous/asynchronous rejection, body readers, and composed
stage-specific failures/short-circuiting
- `npm run build:docs` — 305 pages, including the generated SafeFetch
API page
0 commit comments