Skip to content

Commit 3c50318

Browse files
committed
Fix linting / build issues
1 parent 7090355 commit 3c50318

14 files changed

Lines changed: 311 additions & 206 deletions

.github/workflows/ci.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ jobs:
5959

6060
- name: Tests
6161
run: |
62-
deno test --allow-net --coverage=cov/
62+
deno test --allow-net --coverage=cov/ src/tests
6363
deno coverage cov/
6464
6565
- name: Publish Release Package

deno.json

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
"./mocks": "./src/mocks/mod.ts"
55
},
66
"name": "@foundatiofx/fetchclient",
7+
"license": "MIT",
78
"tasks": {
89
"build": "deno run -A scripts/build.ts",
910
"check": "deno check scripts/*.ts *.ts src/*.ts src/mocks/*.ts",
@@ -17,5 +18,20 @@
1718
"@std/path": "jsr:@std/path@^1.1.4",
1819
"zod": "npm:zod@^4.3.6"
1920
},
20-
"exclude": ["npm", "docs"]
21+
"exclude": ["docs/.vitepress", "docs/package-lock.json", "npm"],
22+
"publish": {
23+
"exclude": [
24+
".claude",
25+
".github",
26+
".vscode",
27+
"AGENTS.md",
28+
"deno.lock",
29+
"docs",
30+
"npm",
31+
"nul",
32+
"samples",
33+
"scripts",
34+
"src/tests"
35+
]
36+
}
2137
}

docs/guide/caching.md

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# Caching
22

3-
FetchClient provides built-in response caching with TTL (time-to-live), cache tags for grouped invalidation, and programmatic cache control.
3+
FetchClient provides built-in response caching with TTL (time-to-live), cache
4+
tags for grouped invalidation, and programmatic cache control.
45

56
## Basic Caching
67

@@ -33,7 +34,8 @@ const cached = await client.getJSON<Todo>(
3334

3435
## Cache Keys
3536

36-
Cache keys are arrays that get joined with colons. This makes it easy to organize and invalidate related entries:
37+
Cache keys are arrays that get joined with colons. This makes it easy to
38+
organize and invalidate related entries:
3739

3840
```ts
3941
// These cache keys become:
@@ -85,7 +87,8 @@ client.cache.clear();
8587

8688
## Cache Tagging
8789

88-
Tags let you group unrelated cache entries and invalidate them together. This is useful when data relationships span different cache keys.
90+
Tags let you group unrelated cache entries and invalidate them together. This is
91+
useful when data relationships span different cache keys.
8992

9093
### Adding Tags
9194

@@ -135,7 +138,8 @@ const entryTags = client.cache.getEntryTags(["posts", "1"]);
135138
## Cache Behavior
136139

137140
- **Automatic cleanup**: Expired entries are removed automatically when accessed
138-
- **Tag cleanup**: Tags are automatically cleaned up when their entries expire or are deleted
141+
- **Tag cleanup**: Tags are automatically cleaned up when their entries expire
142+
or are deleted
139143
- **Memory-based**: Cache is stored in memory and clears on page refresh
140144
- **Per-provider**: Each `FetchClientProvider` has its own cache instance
141145

docs/guide/circuit-breaker.md

Lines changed: 36 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,20 @@
11
# Circuit Breaker
22

3-
The circuit breaker pattern prevents cascading failures when an API goes down. Instead of repeatedly hitting a failing service, the circuit breaker "opens" and immediately rejects requests, giving the service time to recover.
3+
The circuit breaker pattern prevents cascading failures when an API goes down.
4+
Instead of repeatedly hitting a failing service, the circuit breaker "opens" and
5+
immediately rejects requests, giving the service time to recover.
46

57
## Why Use a Circuit Breaker?
68

79
Without a circuit breaker, when a service fails:
10+
811
1. Every request waits for a timeout
912
2. Your app becomes slow and unresponsive
1013
3. You waste resources on doomed requests
1114
4. The failing service gets overwhelmed with retry attempts
1215

1316
With a circuit breaker:
17+
1418
1. After detecting failures, requests fail immediately
1519
2. Your app stays responsive
1620
3. The failing service gets breathing room
@@ -40,8 +44,10 @@ With a circuit breaker:
4044
```
4145

4246
- **CLOSED**: Normal operation. Requests pass through, failures are tracked.
43-
- **OPEN**: Circuit tripped. Requests immediately return 503 (Service Unavailable).
44-
- **HALF_OPEN**: Testing recovery. Limited requests allowed to check if service recovered.
47+
- **OPEN**: Circuit tripped. Requests immediately return 503 (Service
48+
Unavailable).
49+
- **HALF_OPEN**: Testing recovery. Limited requests allowed to check if service
50+
recovered.
4551

4652
## Basic Usage
4753

@@ -52,9 +58,9 @@ const provider = new FetchClientProvider();
5258
provider.setBaseUrl("https://api.example.com");
5359

5460
provider.useCircuitBreaker({
55-
failureThreshold: 5, // Open after 5 failures
56-
openDurationMs: 30000, // Stay open for 30 seconds
57-
successThreshold: 2, // Close after 2 successes in HALF_OPEN
61+
failureThreshold: 5, // Open after 5 failures
62+
openDurationMs: 30000, // Stay open for 30 seconds
63+
successThreshold: 2, // Close after 2 successes in HALF_OPEN
5864
});
5965

6066
const client = provider.getFetchClient();
@@ -71,7 +77,8 @@ const response = await client.getJSON("/users");
7177

7278
## Per-Domain Circuit Breaker
7379

74-
Each domain gets its own circuit breaker, so one failing service doesn't affect others:
80+
Each domain gets its own circuit breaker, so one failing service doesn't affect
81+
others:
7582

7683
```ts
7784
provider.usePerDomainCircuitBreaker({
@@ -89,13 +96,13 @@ await client.getJSON("https://api2.example.com/data"); // Circuit for api2
8996
```ts
9097
provider.useCircuitBreaker({
9198
// When to open the circuit
92-
failureThreshold: 5, // Number of failures before opening (default: 5)
93-
failureWindowMs: 60000, // Time window for counting failures (default: 60000)
99+
failureThreshold: 5, // Number of failures before opening (default: 5)
100+
failureWindowMs: 60000, // Time window for counting failures (default: 60000)
94101

95102
// Recovery
96-
openDurationMs: 30000, // Time to stay OPEN before testing (default: 30000)
97-
successThreshold: 2, // Successes needed to close circuit (default: 2)
98-
halfOpenMaxAttempts: 1, // Max concurrent test requests (default: 1)
103+
openDurationMs: 30000, // Time to stay OPEN before testing (default: 30000)
104+
successThreshold: 2, // Successes needed to close circuit (default: 2)
105+
halfOpenMaxAttempts: 1, // Max concurrent test requests (default: 1)
99106

100107
// What counts as failure
101108
isFailure: (response) => response.status >= 500,
@@ -105,6 +112,7 @@ provider.useCircuitBreaker({
105112
## What Counts as a Failure?
106113

107114
By default, these are treated as failures:
115+
108116
- HTTP 5xx responses (server errors)
109117
- HTTP 429 responses (rate limited)
110118
- Network errors (connection refused, timeout, etc.)
@@ -213,7 +221,9 @@ const failures = breaker.getFailureCount("https://api.example.com/users");
213221
const timeSinceOpen = breaker.getTimeSinceOpen("https://api.example.com/users");
214222

215223
// Time until HALF_OPEN
216-
const timeUntilHalfOpen = breaker.getTimeUntilHalfOpen("https://api.example.com/users");
224+
const timeUntilHalfOpen = breaker.getTimeUntilHalfOpen(
225+
"https://api.example.com/users",
226+
);
217227
```
218228

219229
## Combined with Rate Limiting
@@ -224,16 +234,17 @@ Use both patterns together:
224234
// Rate limiter prevents overwhelming healthy APIs
225235
provider.useRateLimit({
226236
maxRequests: 100,
227-
windowSeconds: 60
237+
windowSeconds: 60,
228238
});
229239

230240
// Circuit breaker stops requests to failing APIs
231241
provider.useCircuitBreaker({
232-
failureThreshold: 5
242+
failureThreshold: 5,
233243
});
234244
```
235245

236-
**Order matters**: Rate limiting happens first. If you're rate limited, the request never reaches the circuit breaker.
246+
**Order matters**: Rate limiting happens first. If you're rate limited, the
247+
request never reaches the circuit breaker.
237248

238249
## Removing Circuit Breaker
239250

@@ -245,9 +256,9 @@ provider.removeCircuitBreaker();
245256

246257
```ts
247258
import {
248-
FetchClientProvider,
249259
CircuitOpenError,
250-
RateLimitError
260+
FetchClientProvider,
261+
RateLimitError,
251262
} from "@foundatiofx/fetchclient";
252263

253264
const provider = new FetchClientProvider();
@@ -290,14 +301,15 @@ async function fetchUser(id: string) {
290301

291302
## Circuit Breaker vs Rate Limiting
292303

293-
| Aspect | Rate Limiter | Circuit Breaker |
294-
|--------|--------------|-----------------|
295-
| **Purpose** | Prevent overloading API | Prevent cascading failures |
296-
| **Trigger** | Request count exceeds limit | Failure count exceeds threshold |
297-
| **When** | Before request | After response |
298-
| **Blocks** | Excess requests | All requests to failing service |
304+
| Aspect | Rate Limiter | Circuit Breaker |
305+
| ------------ | --------------------------- | ----------------------------------------- |
306+
| **Purpose** | Prevent overloading API | Prevent cascading failures |
307+
| **Trigger** | Request count exceeds limit | Failure count exceeds threshold |
308+
| **When** | Before request | After response |
309+
| **Blocks** | Excess requests | All requests to failing service |
299310
| **Recovery** | Automatic after time window | State machine (OPEN → HALF_OPEN → CLOSED) |
300311

301312
Use both together for maximum resilience:
313+
302314
- Rate limiter keeps you within API limits
303315
- Circuit breaker handles when things go wrong

docs/guide/configuration.md

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,14 @@
11
# Configuration
22

3-
FetchClient uses a **default provider** behind the scenes that manages shared configuration, cache, and state for your entire app. You don't need to create or manage providers directly - just call the configuration functions and everything works.
3+
FetchClient uses a **default provider** behind the scenes that manages shared
4+
configuration, cache, and state for your entire app. You don't need to create or
5+
manage providers directly - just call the configuration functions and everything
6+
works.
47

58
## How It Works
69

7-
When you use `new FetchClient()` or call functions like `getJSON()`, they all share the same default provider. This means:
10+
When you use `new FetchClient()` or call functions like `getJSON()`, they all
11+
share the same default provider. This means:
812

913
- **Shared configuration** - Set `baseUrl` once, use it everywhere
1014
- **Shared cache** - Cache entries are available to all clients
@@ -18,8 +22,8 @@ setBaseUrl("https://api.example.com");
1822

1923
// Both use the same configuration and cache
2024
const client = new FetchClient();
21-
await client.getJSON("/users"); // Uses baseUrl
22-
await getJSON("/users"); // Same baseUrl, same cache
25+
await client.getJSON("/users"); // Uses baseUrl
26+
await getJSON("/users"); // Same baseUrl, same cache
2327
```
2428

2529
## Basic Setup
@@ -237,7 +241,12 @@ getCurrentProvider().loading.on((isLoading) => {
237241

238242
```ts
239243
// user-service.ts
240-
import { deleteJSON, getCache, getJSON, postJSON } from "@foundatiofx/fetchclient";
244+
import {
245+
deleteJSON,
246+
getCache,
247+
getJSON,
248+
postJSON,
249+
} from "@foundatiofx/fetchclient";
241250

242251
export async function getUsers() {
243252
const { data } = await getJSON<User[]>("/users", {
@@ -262,4 +271,7 @@ export async function deleteUser(id: number) {
262271

263272
## Advanced: Custom Providers
264273

265-
For advanced use cases like connecting to multiple APIs with different configurations, you can create separate `FetchClientProvider` instances. See the [API reference](https://jsr.io/@foundatiofx/fetchclient/doc/~/FetchClientProvider) for details.
274+
For advanced use cases like connecting to multiple APIs with different
275+
configurations, you can create separate `FetchClientProvider` instances. See the
276+
[API reference](https://jsr.io/@foundatiofx/fetchclient/doc/~/FetchClientProvider)
277+
for details.

docs/guide/error-handling.md

Lines changed: 17 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# Error Handling
22

3-
FetchClient provides flexible error handling with support for expected status codes, custom error callbacks, and RFC 7807 Problem Details.
3+
FetchClient provides flexible error handling with support for expected status
4+
codes, custom error callbacks, and RFC 7807 Problem Details.
45

56
## Default Behavior
67

@@ -93,23 +94,23 @@ const response = await client.postJSON("/api/users", {
9394
});
9495

9596
if (!response.ok) {
96-
console.log(response.problem.title); // "Validation Error"
97-
console.log(response.problem.detail); // "The request was invalid"
98-
console.log(response.problem.status); // 400
99-
console.log(response.problem.errors); // { email: ["Invalid email format"] }
97+
console.log(response.problem.title); // "Validation Error"
98+
console.log(response.problem.detail); // "The request was invalid"
99+
console.log(response.problem.status); // 400
100+
console.log(response.problem.errors); // { email: ["Invalid email format"] }
100101
}
101102
```
102103

103104
### Problem Details Structure
104105

105106
```ts
106107
interface ProblemDetails {
107-
type?: string; // URI identifying the problem type
108-
title?: string; // Short human-readable summary
109-
status?: number; // HTTP status code
110-
detail?: string; // Detailed explanation
111-
instance?: string; // URI identifying this occurrence
112-
errors?: Record<string, string[]>; // Field-level errors
108+
type?: string; // URI identifying the problem type
109+
title?: string; // Short human-readable summary
110+
status?: number; // HTTP status code
111+
detail?: string; // Detailed explanation
112+
instance?: string; // URI identifying this occurrence
113+
errors?: Record<string, string[]>; // Field-level errors
113114
}
114115
```
115116

@@ -134,7 +135,7 @@ problem.errors = {
134135
Validate request data before sending:
135136

136137
```ts
137-
import { setModelValidator, ProblemDetails } from "@foundatiofx/fetchclient";
138+
import { ProblemDetails, setModelValidator } from "@foundatiofx/fetchclient";
138139

139140
setModelValidator(async (data) => {
140141
if (!data) return null;
@@ -170,7 +171,7 @@ if (!response.ok) {
170171

171172
```ts
172173
import { z } from "zod";
173-
import { setModelValidator, ProblemDetails } from "@foundatiofx/fetchclient";
174+
import { ProblemDetails, setModelValidator } from "@foundatiofx/fetchclient";
174175

175176
const UserSchema = z.object({
176177
email: z.string().email("Invalid email format"),
@@ -275,9 +276,9 @@ try {
275276

276277
```ts
277278
import {
279+
CircuitOpenError,
278280
FetchClient,
279281
FetchClientProvider,
280-
CircuitOpenError,
281282
RateLimitError,
282283
} from "@foundatiofx/fetchclient";
283284

@@ -290,7 +291,7 @@ const client = provider.getFetchClient();
290291

291292
async function apiRequest<T>(
292293
url: string,
293-
options?: RequestOptions
294+
options?: RequestOptions,
294295
): Promise<{ data: T | null; error: string | null }> {
295296
try {
296297
const response = await client.getJSON<T>(url, {
@@ -307,7 +308,7 @@ async function apiRequest<T>(
307308
case 400:
308309
return {
309310
data: null,
310-
error: response.problem.detail || "Invalid request"
311+
error: response.problem.detail || "Invalid request",
311312
};
312313
case 401:
313314
// Redirect to login

0 commit comments

Comments
 (0)