#5: createCoreFetch default responseShape flipped to 'body'
Orval's `client: 'fetch'` (with the no-`includeHttpResponseReturnType` default) emits mutator signatures expecting the parsed body — so making `'body'` the default means the no-arg `createCoreFetch()` form is the right answer for the typical Orval-driven derived project. Callers that need Response metadata opt in via `responseShape: 'wrapped'`. Aligns README + docstring + the inline default. Tests updated to assert default-`'body'` and explicit-`'wrapped'`. Bump to 0.4.0 (BREAKING — default flipped). Downstream consumers on the default need to either (a) add explicit `responseShape: 'wrapped'` to preserve old behaviour, or (b) migrate callers to the body shape. fewo-webapp's existing `responseShape: 'body'` override is now redundant and will be dropped in a follow-up. Closes #5 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
90c5ca2248
commit
fd451fd452
4 changed files with 28 additions and 22 deletions
14
README.md
14
README.md
|
|
@ -43,12 +43,16 @@ webapp-scaffold-postprocess-openapi && \
|
|||
orval
|
||||
```
|
||||
|
||||
## `createCoreFetch` (v0.2)
|
||||
## `createCoreFetch` (v0.4 — default flipped to `'body'`)
|
||||
|
||||
Orval's `client: 'fetch'` calls a mutator `coreFetch<T>(url, init)` and
|
||||
expects `{data, status, headers}` back. `createCoreFetch(opts)` returns
|
||||
such a function, with credentials/CSRF headers / 204 / content-type
|
||||
handling already wired, and hooks for project-specific concerns:
|
||||
Orval's `client: 'fetch'` calls a mutator `coreFetch<T>(url, init)`. The
|
||||
return shape is configurable; **the default is `'body'`** (returns the
|
||||
parsed JSON body directly, matching Orval's
|
||||
`includeHttpResponseReturnType: false`). Pass `responseShape: 'wrapped'`
|
||||
to opt back into `{data, status, headers}` when callers need the
|
||||
Response metadata. `createCoreFetch(opts)` returns such a function, with
|
||||
credentials/CSRF headers / 204 / content-type handling already wired,
|
||||
and hooks for project-specific concerns:
|
||||
|
||||
```ts
|
||||
import { createCoreFetch } from '@uschuster/webapp-scaffold/core-fetch';
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@uschuster/webapp-scaffold",
|
||||
"version": "0.3.7",
|
||||
"version": "0.4.0",
|
||||
"description": "Shared build scripts + Vite config factories for webapp-template-derived projects.",
|
||||
"type": "module",
|
||||
"bin": {
|
||||
|
|
|
|||
|
|
@ -17,22 +17,22 @@ function jsonResponse(body: unknown, init: ResponseInit = {}): Response {
|
|||
}
|
||||
|
||||
describe('createCoreFetch — response shapes', () => {
|
||||
it("'wrapped' default returns { data, status, headers }", async () => {
|
||||
it("'body' default returns the parsed body directly", async () => {
|
||||
const cf = createCoreFetch({ fetchImpl: () => Promise.resolve(jsonResponse({ ok: 1 })) });
|
||||
const r = await cf<{ ok: number }>('/x');
|
||||
expect(r).toEqual({ ok: 1 });
|
||||
});
|
||||
|
||||
it("'wrapped' opt-in returns { data, status, headers }", async () => {
|
||||
const cf = createCoreFetch({
|
||||
responseShape: 'wrapped',
|
||||
fetchImpl: () => Promise.resolve(jsonResponse({ ok: 1 })),
|
||||
});
|
||||
const r = await cf<{ data: { ok: number }; status: number }>('/x');
|
||||
expect(r.data).toEqual({ ok: 1 });
|
||||
expect(r.status).toBe(200);
|
||||
});
|
||||
|
||||
it("'body' returns the parsed body directly", async () => {
|
||||
const cf = createCoreFetch({
|
||||
responseShape: 'body',
|
||||
fetchImpl: () => Promise.resolve(jsonResponse({ ok: 1 })),
|
||||
});
|
||||
const r = await cf<{ ok: number }>('/x');
|
||||
expect(r).toEqual({ ok: 1 });
|
||||
});
|
||||
|
||||
it('non-JSON 2xx returns the body as plain text', async () => {
|
||||
const cf = createCoreFetch({
|
||||
responseShape: 'body',
|
||||
|
|
|
|||
|
|
@ -3,10 +3,12 @@
|
|||
// Orval's `client: 'fetch'` emits functions that call
|
||||
// coreFetch<T>(url, init)
|
||||
// and expect one of:
|
||||
// • { data, status, headers } back (responseShape: 'wrapped', default —
|
||||
// matches orval's default `includeHttpResponseReturnType: true`)
|
||||
// • the parsed body directly (responseShape: 'body' — matches orval's
|
||||
// `includeHttpResponseReturnType: false`, fewo's convention)
|
||||
// • the parsed body directly (responseShape: 'body', DEFAULT — matches
|
||||
// orval's `includeHttpResponseReturnType: false`, the Orval-driven
|
||||
// "no extra ceremony" path)
|
||||
// • { data, status, headers } back (responseShape: 'wrapped' — matches
|
||||
// orval's `includeHttpResponseReturnType: true`; opt in when callers
|
||||
// need the Response metadata)
|
||||
//
|
||||
// Baseline wired in:
|
||||
// • credentials: 'include' — cookies always sent
|
||||
|
|
@ -29,7 +31,7 @@ export interface CoreFetchOptions {
|
|||
/** Prefix for every request; strip trailing slash. */
|
||||
baseUrl?: string;
|
||||
|
||||
/** `'wrapped'` returns { data, status, headers } (default); `'body'` returns the parsed body. */
|
||||
/** `'body'` returns the parsed body (default); `'wrapped'` returns { data, status, headers }. */
|
||||
responseShape?: ResponseShape;
|
||||
|
||||
/**
|
||||
|
|
@ -101,7 +103,7 @@ export function createCoreFetch(opts: CoreFetchOptions = {}): CoreFetch {
|
|||
const callFetch: typeof fetch = opts.fetchImpl
|
||||
? opts.fetchImpl
|
||||
: ((...args) => fetch(...(args as Parameters<typeof fetch>))) as typeof fetch;
|
||||
const shape: ResponseShape = opts.responseShape ?? 'wrapped';
|
||||
const shape: ResponseShape = opts.responseShape ?? 'body';
|
||||
|
||||
function wrap(data: unknown, status: number, headers: Headers): unknown {
|
||||
return shape === 'body' ? data : { data, status, headers };
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue