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
Other packages hook into these middleware points. `fs-loading` registers request + response + error middleware to track loading state. `fs-dialog` can register error middleware to show error dialogs. You can stack as many middleware handlers as you need — they all run independently.
173
173
:::
174
174
175
+
## Middleware guard (`guarded`)
176
+
177
+
`fs-http` invokes middleware **synchronously and un-caught, by design** — the library stays loud so a bug in a middleware body is never silently eaten inside the transport layer. The consequence: if a middleware body throws (a toast that blows up, a store write, a router push, a `JSON.parse` of a cache hash), that throw escapes into the interceptor chain. On the response path it **rejects a resolved 200**; on the error path it **replaces the original `AxiosError` with the middleware's throw**, masking the real API failure.
178
+
179
+
`guarded()` is the **consumer-side, opt-in** defense. Wrap a middleware body at its registration site and a throw from the body is caught, reported, and swallowed — the interceptor chain is never corrupted. Loud library, defensive consumer.
// A throwing response body would otherwise reject a resolved 200 — guarded()
187
+
// contains it so the successful response still resolves.
188
+
http.registerResponseMiddleware(
189
+
guarded((response) => {
190
+
showToast(`Loaded ${response.config.url}`); // may throw — no longer fatal
191
+
}),
192
+
);
193
+
194
+
// A throwing error body would otherwise mask the real AxiosError — guarded()
195
+
// contains it so the caller still rejects with the original error.
196
+
http.registerResponseErrorMiddleware(
197
+
guarded((error) => {
198
+
openErrorDialog(error); // may throw — the 500 still surfaces to the caller
199
+
}),
200
+
);
201
+
```
202
+
203
+
All three middleware types share the `(arg) => void` shape, so one generic wraps any of them and stays assignable to the source type with **zero casts** — `guarded(reqBody)`, `guarded(resBody)`, and `guarded(errBody)` each infer their argument type from the body you pass.
204
+
205
+
### Custom error handling
206
+
207
+
By default a swallowed throw is logged loudly via `console.error` (visible to any error tracker that hooks `console`). It is never re-thrown — re-throwing would re-open the exact failure `guarded()` closes. Pass a custom `GuardedMiddlewareErrorHandler` to route the failure elsewhere:
Wrapping the interceptor loops in try/catch inside `fs-http` was rejected (2026-05-13): it would swallow every consumer's middleware bug silently, at the library layer, with no way to opt back into loud behaviour. `guarded()` inverts that — the library stays loud, and each consumer decides, explicitly at each registration site, which bodies to protect.
225
+
:::
226
+
175
227
## File Operations
176
228
177
229
`downloadRequest` and `previewRequest` are **transport-only** — they GET an endpoint as a `Blob` and return the full `AxiosResponse<Blob>`. Neither touches the DOM. There is no browser save dialog and no object-URL management inside fs-http; the consumer owns that orchestration (fs-packages issue #59). The two names share identical transport (`responseType: 'blob'`); the separate name communicates intent (download = save-to-disk, preview = inline-display).
|`guarded(fn, onError?)`|`<T>(fn: (arg: T) => void, onError?) => (arg: T) => void`| Wraps a middleware body so a throw is caught, reported, and swallowed instead of corrupting the interceptor chain. See [Middleware guard](#middleware-guard-guarded). |
324
+
|`GuardedMiddlewareErrorHandler`|`(error: unknown) => void`| Handler type for `guarded`'s optional second argument; defaults to a loud `console.error`. Must not re-throw. |
Copy file name to clipboardExpand all lines: packages/cached-adapter-store/CHANGELOG.md
+6Lines changed: 6 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -1,5 +1,11 @@
1
1
# @script-development/fs-cached-adapter-store
2
2
3
+
## 0.2.4 — 2026-07-02
4
+
5
+
### Patch Changes
6
+
7
+
-**Peer-range widening for `@script-development/fs-http``^0.5.0`.**`fs-http` published a minor (0.5.0, the additive `guarded()` middleware guard). Pre-1.0 caret semantics require every `fs-http` consumer to widen its accepted range; no behavioural change. Mechanical cascade per fs-packages `CLAUDE.md` § Versioning Discipline.
"description": "Higher-order factory wrapping @script-development/fs-adapter-store with hash-bumping cache-check that suppresses redundant retrieveAll GETs at source",
Copy file name to clipboardExpand all lines: packages/http/CHANGELOG.md
+10Lines changed: 10 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -1,5 +1,15 @@
1
1
# @script-development/fs-http
2
2
3
+
## 0.5.0 — 2026-07-02
4
+
5
+
### Minor Changes
6
+
7
+
-**New export: `guarded()` — consumer-side middleware guard wrapper.** A higher-order function that wraps an `fs-http` middleware body in try/catch so a side-effect throw (toast, store write, router push, cache-hash parse) cannot corrupt the interceptor chain — it can no longer reject a resolved 200 nor mask the original API error on the error path. All three middleware types (`RequestMiddlewareFunc`, `ResponseMiddlewareFunc`, `ResponseErrorMiddlewareFunc`) share the `(arg) => void` shape, so one generic wraps any of them and stays assignable to the source type with zero casts: `service.registerResponseMiddleware(guarded((response) => { ... }))`.
8
+
- The library stays **sync-only and loud** — `createHttpService` and the interceptor loops are unchanged (the 2026-05-13 rejection of library-side try/catch holds). `guarded()` is **opt-in at the consumer's registration site**: loud library, defensive consumer.
9
+
- The default error handler surfaces the swallowed failure via `console.error` (visible to error trackers) and never re-throws. Pass a custom `GuardedMiddlewareErrorHandler` to route the failure elsewhere.
10
+
- Also exports the `GuardedMiddlewareErrorHandler` type.
11
+
- Additive and non-breaking — no existing API changed.
0 commit comments