core-fetch: baseUrl accepts () => string for lazy resolution
Lets consumers pass `() => import.meta.env.BASE_URL` without orval's CJS bundle path blowing up on the top-level `import.meta` reference. The getter is invoked at request time rather than factory time, so the mutator file can be loaded by orval (which bundles to CJS) without evaluating `import.meta`. Closes uwe.admin/webapp-template#21 (scaffold side). Bump to v0.5.0 — additive change (string form still works) but new shape for `CoreFetchOptions.baseUrl` is a public API change. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
8677faf54b
commit
b00a4320a7
4 changed files with 38 additions and 7 deletions
4
package-lock.json
generated
4
package-lock.json
generated
|
|
@ -1,12 +1,12 @@
|
|||
{
|
||||
"name": "@uschuster/webapp-scaffold",
|
||||
"version": "0.3.6",
|
||||
"version": "0.5.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@uschuster/webapp-scaffold",
|
||||
"version": "0.3.6",
|
||||
"version": "0.5.0",
|
||||
"license": "UNLICENSED",
|
||||
"bin": {
|
||||
"webapp-scaffold-fetch-openapi": "bin/fetch-openapi.sh",
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@uschuster/webapp-scaffold",
|
||||
"version": "0.4.2",
|
||||
"version": "0.5.0",
|
||||
"description": "Shared build scripts + Vite config factories for webapp-template-derived projects.",
|
||||
"type": "module",
|
||||
"bin": {
|
||||
|
|
|
|||
|
|
@ -173,4 +173,23 @@ describe('createCoreFetch — request shape', () => {
|
|||
await cf('/v1/x');
|
||||
expect(captured).toBe('https://api.example.com/v1/x');
|
||||
});
|
||||
|
||||
it('baseUrl getter is resolved at request time, not factory time', async () => {
|
||||
const captured: string[] = [];
|
||||
let dynamicBase = 'https://first.example.com';
|
||||
const cf = createCoreFetch({
|
||||
baseUrl: () => dynamicBase,
|
||||
fetchImpl: ((url: string) => {
|
||||
captured.push(url);
|
||||
return Promise.resolve(jsonResponse({}));
|
||||
}) as unknown as typeof fetch,
|
||||
});
|
||||
await cf('/v1/x');
|
||||
dynamicBase = 'https://second.example.com/';
|
||||
await cf('/v1/y');
|
||||
expect(captured).toEqual([
|
||||
'https://first.example.com/v1/x',
|
||||
'https://second.example.com/v1/y',
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -28,8 +28,16 @@ export interface CoreFetchRequest {
|
|||
}
|
||||
|
||||
export interface CoreFetchOptions {
|
||||
/** Prefix for every request; strip trailing slash. */
|
||||
baseUrl?: string;
|
||||
/**
|
||||
* Prefix for every request; trailing slash stripped at resolve time.
|
||||
*
|
||||
* Accepts a string OR a getter so consumers that derive the base from
|
||||
* `import.meta.env.BASE_URL` (or any other late-bound source) can pass
|
||||
* the getter form. The getter is invoked at request time, never at
|
||||
* factory-construction time — which keeps orval's CJS bundling of the
|
||||
* mutator file from blowing up on top-level `import.meta` references.
|
||||
*/
|
||||
baseUrl?: string | (() => string);
|
||||
|
||||
/** `'body'` returns the parsed body (default); `'wrapped'` returns { data, status, headers }. */
|
||||
responseShape?: ResponseShape;
|
||||
|
|
@ -97,7 +105,11 @@ export type CoreFetch = CoreFetchWrapped & CoreFetchBody;
|
|||
const MUTATING = new Set(['POST', 'PUT', 'PATCH', 'DELETE']);
|
||||
|
||||
export function createCoreFetch(opts: CoreFetchOptions = {}): CoreFetch {
|
||||
const base = (opts.baseUrl ?? '').replace(/\/$/, '');
|
||||
// Lazy base resolution — see CoreFetchOptions.baseUrl JSDoc.
|
||||
const resolveBase = (): string => {
|
||||
const raw = typeof opts.baseUrl === 'function' ? opts.baseUrl() : (opts.baseUrl ?? '');
|
||||
return raw.replace(/\/$/, '');
|
||||
};
|
||||
// Resolve fetch at call time, not construction time, so tests that
|
||||
// `vi.stubGlobal('fetch', ...)` after module import see the stub.
|
||||
const callFetch: typeof fetch = opts.fetchImpl
|
||||
|
|
@ -135,7 +147,7 @@ export function createCoreFetch(opts: CoreFetchOptions = {}): CoreFetch {
|
|||
|
||||
let r: Response;
|
||||
try {
|
||||
r = await callFetch(`${base}${url}`, {
|
||||
r = await callFetch(`${resolveBase()}${url}`, {
|
||||
credentials: 'include',
|
||||
...init,
|
||||
headers,
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue