diff --git a/README.md b/README.md index c6e165a..cb695fe 100644 --- a/README.md +++ b/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(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(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'; diff --git a/package.json b/package.json index e9c21d8..564f14c 100644 --- a/package.json +++ b/package.json @@ -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": { diff --git a/src/core-fetch.test.ts b/src/core-fetch.test.ts index 1bd87ce..0b6890b 100644 --- a/src/core-fetch.test.ts +++ b/src/core-fetch.test.ts @@ -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', diff --git a/src/core-fetch.ts b/src/core-fetch.ts index f7c9da8..3dc5afc 100644 --- a/src/core-fetch.ts +++ b/src/core-fetch.ts @@ -3,10 +3,12 @@ // Orval's `client: 'fetch'` emits functions that call // coreFetch(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))) 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 };