Merge origin/main into branch

This commit is contained in:
Casey Collier 2025-11-12 22:41:32 -05:00
commit 2c1b42c1ac
4 changed files with 33 additions and 16 deletions

View file

@ -117,6 +117,13 @@ const { data } = await tf.patch('https://api.example.com/users/123', {
title: 'Director of Engineering'
})
// PATCH request
const { data } = await tf.patch('https://api.example.com/users/123', {
body: {
title: 'Director of Engineering'
}
})
// DELETE request
const { data, response } = await tf.delete('https://api.example.com/users/123')
```
@ -355,4 +362,4 @@ MIT License - see [LICENSE](LICENSE) for details.
---
**TypedFetch**: Because life's too short for complex HTTP clients. 🚀
**TypedFetch**: Because life's too short for complex HTTP clients. 🚀

View file

@ -70,8 +70,8 @@ export interface ResourceBuilderOptions {
}
interface ResourceRequester {
request(method: string, url: string, init?: RequestInit, bodySample?: unknown): Promise<{
data: any
request<T>(method: string, url: string, init?: RequestInit, bodySample?: unknown): Promise<{
data: T
response: Response
}>
}
@ -85,10 +85,21 @@ export function createResource<TDefinition extends ResourceDefinition>(
const normalizedBase = normalizePath(basePath, options.trailingSlash)
const resource: Record<string, any> = {}
for (const [name, config] of Object.entries(definition)) {
resource[name] = async (
args: ResourceCallArgs<any, Record<string, string | number>, Record<string, unknown>> = {}
) => {
for (const name of Object.keys(definition) as Array<keyof TDefinition>) {
type Config = TDefinition[typeof name]
type BodyType = Config extends ResourceMethodConfig<any, infer TBody, any, any> ? TBody : never
type ParamsType = Config extends ResourceMethodConfig<any, any, infer TParams, any>
? TParams
: Record<string, string | number>
type QueryType = Config extends ResourceMethodConfig<any, any, any, infer TQuery>
? TQuery
: Record<string, unknown>
type ResponseType = Config extends ResourceMethodConfig<infer TResponse, any, any, any>
? TResponse
: unknown
const config = definition[name] as Config
resource[name as string] = async (args: ResourceCallArgs<BodyType, ParamsType, QueryType> = {}) => {
const method = (config.method || 'GET').toUpperCase()
const path = joinPaths(normalizedBase, config.path, options.trailingSlash)
const resolvedPath = applyParams(path, args.params)
@ -100,12 +111,12 @@ export function createResource<TDefinition extends ResourceDefinition>(
const finalUrl = `${resolvedPath}${query}`
const shouldUseJson = config.json ?? true
const serializer = config.serializeBody
const serializer = config.serializeBody as ((body: BodyType) => BodyInit | undefined) | undefined
const hasBody = args.body !== undefined
const prepared = hasBody
? serializer
? { bodyInit: serializer(args.body), sample: args.body }
: prepareBodyPayload(args.body, { json: shouldUseJson })
? { bodyInit: serializer(args.body as BodyType), sample: args.body }
: prepareBodyPayload(args.body as BodyType, { json: shouldUseJson })
: undefined
const init: RequestInit = {
@ -120,7 +131,7 @@ export function createResource<TDefinition extends ResourceDefinition>(
init.body = prepared?.bodyInit ?? null
}
const result = await requester.request(
const result = await requester.request<ResponseType>(
method,
finalUrl,
init,
@ -132,7 +143,7 @@ export function createResource<TDefinition extends ResourceDefinition>(
response: result.response
}
}
return result as { data: unknown; response: Response }
return result
}
}

View file

@ -91,7 +91,7 @@ export class RevolutionaryTypedFetch<TEndpoints extends EndpointTypeMap = Endpoi
*/
create(config: TypedFetchConfig): RevolutionaryTypedFetch<TEndpoints> {
const mergedConfig = mergeConfig(this.config, config)
return new RevolutionaryTypedFetch(mergedConfig)
return new RevolutionaryTypedFetch<TEndpoints>(mergedConfig)
}
// REAL runtime type tracking
@ -237,7 +237,7 @@ export class RevolutionaryTypedFetch<TEndpoints extends EndpointTypeMap = Endpoi
): ResourceInstance<TDefinition> {
return createResource(this, path, definition, options)
}
private resolveUrl(url: string): string {
// Use baseURL from config or instance
const baseURL = this.config.request.baseURL || this.baseURL
@ -1182,4 +1182,4 @@ export class RevolutionaryTypedFetch<TEndpoints extends EndpointTypeMap = Endpoi
return value * (multipliers[unit] || 1)
}
}
}

View file

@ -1,5 +1,4 @@
import { describe, expect, it } from 'vitest'
import { mergePartialConfig, prepareBodyPayload, resolveBodyArgs } from '../src/core/body-utils.js'
import type { TypedFetchConfig } from '../src/types/config.js'