Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
162 changes: 162 additions & 0 deletions .github/CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
# 📄 `CONTRIBUTING.md`

````md
# Contributing to vfetch

Thanks for your interest in contributing.

This project is designed to be **strict, predictable, and production-grade**. Contributions must follow the guidelines below to be accepted.

---

## 🛠️ Getting Started

Clone the repo and install dependencies:

```bash
git clone https://github.com/Gab-codes/vfetch.git
cd vfetch
npm install
```
````

Run tests:

```bash
npm run test
```

Run coverage:

```bash
npm run coverage
```

---

## 📁 Project Structure

```
src/ → core implementation
tests/ → unit tests
tests/integration/ → integration tests
```

Do **not** mix concerns:

- Unit tests stay in `tests/`
- Full flow / real API tests go in `tests/integration/`

---

## 📏 Rules & Standards

### 1. No breaking changes without discussion

If your change alters behavior, open an issue first.

---

### 2. Tests are mandatory

- Every feature or fix **must include tests**
- No exceptions
- If it’s not tested, it won’t be merged

---

### 3. Keep types strict

- Avoid `any` unless absolutely necessary
- Prefer explicit types
- Maintain full TypeScript safety

---

### 4. Do not modify unrelated code

Only change what your PR is responsible for.

---

### 5. Maintain existing design decisions

This library prioritizes:

- Predictable `{ ok: boolean }` responses
- No throwing for HTTP errors
- Transport-layer responsibility only (no schema validation)

Do not introduce patterns that break this philosophy.

---

### 6. Keep it lightweight

This is a **zero-dependency** library.

Do not add dependencies unless absolutely justified.

---

## 🧪 Testing Guidelines

- Use **Vitest**
- Use mocks for controlled behavior
- Keep tests isolated and deterministic
- Avoid unnecessary real API calls (only in integration tests)

---

## 🚀 Pull Request Process

1. Fork the repo
2. Create a new branch:

```bash
git checkout -b feat/your-feature
```

3. Make your changes
4. Add/Update tests
5. Ensure everything passes:

```bash
npm run coverage
```

6. Open a PR

---

## ✅ PR Requirements

- All tests must pass
- Coverage must not decrease
- No TypeScript errors
- Clear description of what changed and why

---

## ❌ What Will Be Rejected

- PRs without tests
- Introducing unnecessary dependencies
- Weak typing (`any` abuse)
- Breaking core API design without discussion

---

## 💡 Philosophy

vfetch is built to be:

- Predictable
- Lightweight
- Safe under concurrency

Keep that in mind when contributing.

---

## Thanks for contributing.
8 changes: 7 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -368,6 +368,12 @@ const { mutate } = useMutation({

---

## Contributing

Contributions are welcome. Please read the [Contributing Guide](.github/CONTRIBUTING.md) before opening a PR.

---

## 📄 License

MIT
MIT [License](LICENSE.md)
12 changes: 6 additions & 6 deletions src/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ export class VfetchClient {
/**
* Internal request handler that implements the full request lifecycle.
*/
private async request<T>(
private async request<T = any>(
path: string,
options: RequestOptions & {
method: string;
Expand Down Expand Up @@ -278,7 +278,7 @@ export class VfetchClient {
* @param path - The URL path relative to baseURL
* @param options - Optional request configuration
*/
async get<T>(path: string, options?: RequestOptions): Promise<VfetchResponse<T>> {
async get<T = any>(path: string, options?: RequestOptions): Promise<VfetchResponse<T>> {
return this.request<T>(path, { ...options, method: "GET" });
}

Expand All @@ -289,7 +289,7 @@ export class VfetchClient {
* @param body - The request body, serialized as JSON
* @param options - Optional request configuration
*/
async post<T>(path: string, body?: unknown, options?: RequestOptions): Promise<VfetchResponse<T>> {
async post<T = any>(path: string, body?: unknown, options?: RequestOptions): Promise<VfetchResponse<T>> {
return this.request<T>(path, { ...options, method: "POST", body });
}

Expand All @@ -300,7 +300,7 @@ export class VfetchClient {
* @param body - The request body, serialized as JSON
* @param options - Optional request configuration
*/
async patch<T>(path: string, body?: unknown, options?: RequestOptions): Promise<VfetchResponse<T>> {
async patch<T = any>(path: string, body?: unknown, options?: RequestOptions): Promise<VfetchResponse<T>> {
return this.request<T>(path, { ...options, method: "PATCH", body });
}

Expand All @@ -311,7 +311,7 @@ export class VfetchClient {
* @param body - The request body, serialized as JSON
* @param options - Optional request configuration
*/
async put<T>(path: string, body?: unknown, options?: RequestOptions): Promise<VfetchResponse<T>> {
async put<T = any>(path: string, body?: unknown, options?: RequestOptions): Promise<VfetchResponse<T>> {
return this.request<T>(path, { ...options, method: "PUT", body });
}

Expand All @@ -321,7 +321,7 @@ export class VfetchClient {
* @param path - The URL path relative to baseURL
* @param options - Optional request configuration
*/
async delete<T>(path: string, options?: RequestOptions): Promise<VfetchResponse<T>> {
async delete<T = any>(path: string, options?: RequestOptions): Promise<VfetchResponse<T>> {
return this.request<T>(path, { ...options, method: "DELETE" });
}
}
9 changes: 7 additions & 2 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,16 @@ import { VfetchConfig } from "./types";
* ```ts
* const api = createClient({
* baseURL: "https://api.example.com",
* timeout: 5000,
* retry: 3,
* timeout: 5000, // optional - request timeout in ms
* retry: 3, // optional - retry network failures up to 3 times
* retryDelay: 1000, // optional - delay between retries in ms
* });
*
* // With type annotation (fully typed)
* const result = await api.get<User[]>("/users");
*
* // Without type annotation (response.data defaults to 'any')
* const result = await api.get("/users");
* ```
*/
export function createClient(config: VfetchConfig): VfetchClient {
Expand Down
4 changes: 2 additions & 2 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
* Represents a successful response from vfetch.
* @template T The type of the data returned by the server.
*/
export interface VfetchSuccess<T> {
export interface VfetchSuccess<T = any> {
/** Indicates the request was successful. */
readonly ok: true;
/** The parsed JSON data from the response. */
Expand All @@ -27,7 +27,7 @@ export interface VfetchError<E = string | Record<string, any>> {
/**
* The union type of all possible vfetch responses.
*/
export type VfetchResponse<T> = VfetchSuccess<T> | VfetchError;
export type VfetchResponse<T = any> = VfetchSuccess<T> | VfetchError;

/**
* Configuration options for the vfetch client instance.
Expand Down
Loading