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
Richer errors
Issue now has optional received; ValidationError / UnexpectedError / FnError all serialize via toJSON()
Schema messages include value previews (types, email/URL/regex/check, unions with branch N: labels)
Paths name the door: fn.input…, model.create…
HTTP edge
encodeError → ValidationError 400, FnError status/422, UnexpectedError 500
createHandler / handler return JSON bodies instead of rethrowing
http.err status is stamped onto FnError at mint (read from the original declaration, since asType drops the symbol)
Breaking: input paths now include .input (e.g. fnt.multi.input.a).
Copy file name to clipboardExpand all lines: packages/experimental/README.md
+13-1Lines changed: 13 additions & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -109,7 +109,19 @@ What this buys, all Effect-inspired but with plain functions:
109
109
-**Failure vs defect.** A thrown `c.error(...)` is a `FnError` - a domain outcome, tagged, serializable (`{ tag, data, trail }` survives a wire). Once a fn declares `errors`, any *untagged* throw escaping its body is a bug and comes out as `UnexpectedError` with the original on `cause`. Callers never string-match to tell the two apart.
110
110
-**Typed recovery.**`fn.try(input)` returns `{ ok: true, value } | { ok: false, error }` where `error` is the union of declared errors - TS narrows on `error.tag`. Defects and contract violations still throw. `FnErrors<typeof fn>` gives the union for catch sites.
111
111
-**The trail.** As an error crosses fn frames it collects their keys - `["audit.log", "profile.update", "capability.exec"]` - origin first, so nothing about where a failure started is ever lost.
112
-
-**All issues, not the first.** Validation collects every bad field / tuple position in one `ValidationError.issues` list.
112
+
-**All issues, not the first.** Validation collects every bad field / tuple position in one `ValidationError.issues` list. Paths name the contract door (`sign_in.email.input.email`), and each issue can carry a `received` preview:
113
+
114
+
```ts
115
+
// sign_in.email.input.email: expected an email address, received "nope"
116
+
// sign_in.email.input.password: expected string, received number (1)
117
+
err.issues;
118
+
// [
119
+
// { path: "sign_in.email.input.email", message: "expected an email address, received \"nope\"", received: "\"nope\"" },
120
+
// { path: "sign_in.email.input.password", message: "expected string, received number (1)", received: "1" },
121
+
// ]
122
+
```
123
+
124
+
At the HTTP edge (`createHandler` / `handler`), `ValidationError` becomes `400` with that JSON body, `FnError` uses `http.err` status (or `422`), and `UnexpectedError` becomes `500`.
0 commit comments