Skip to content

Commit 483e366

Browse files
authored
refactor: Download+url types (#93)
2 parents b7a5121 + a4a6a86 commit 483e366

File tree

20 files changed

+231
-168
lines changed

20 files changed

+231
-168
lines changed

CHANGELOG.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# Releases
22

33
## Contents
4+
- [v0.3.3-alpha.1](#v0-3-3-alpha-1)
45
- [v0.3.3-alpha.0](#v0-3-3-alpha-0)
56
- [v0.3.2](#v0-3-2)
67
- [v0.3.1](#v0-3-1)
@@ -16,6 +17,18 @@
1617
- [v0.1.0](#v0-1-0)
1718
- [v0.0.8](#v0-0-8)
1819

20+
## v0.3.3-alpha.1
21+
22+
- Make response optional for both `url` and `download`, but at the same time enforce `string` for `url`, and `Blob` for download if defined.
23+
- Add validation before `url` and `download` return.
24+
- Add more and better examples to guide for download + url.
25+
- Internal; Improve default destructuring in RequestClient.
26+
- Internal; More direct cache-pending return.
27+
- Internal; Cleanup pending cache faster.
28+
- Internal; More iteratively merging headers for nicer readability and sanitization.
29+
- Internal; Add AbortSignal.any execution attempt.
30+
- Internal; Reduce excessive checks and variables in validator function.
31+
1932
## v0.3.3-alpha.0
2033

2134
- Bump @standard-schema/spec@1.1.0.

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
[![minzip](https://deno.bundlejs.com/badge?q=wiretyped@latest)](https://bundlejs.com/?q=wiretyped@latest)
1616

1717

18+
[![npm](https://img.shields.io/npm/v/wiretyped.svg)](https://www.npmjs.com/package/wiretyped) [![JSR](https://jsr.io/badges/@kasperrt/wiretyped)](https://jsr.io/@kasperrt/wiretyped)
19+
1820
Universal fetch-based, typed HTTP client with error-first ergonomics, retries, caching, SSE, and Standard Schema validation.
1921

2022
https://wiretyped.io

docs/.vitepress/theme/style.css

Lines changed: 2 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -144,45 +144,12 @@
144144
display: none;
145145
}
146146

147-
.wt-hero-badges-row {
148-
display: flex;
149-
flex-wrap: wrap;
150-
gap: 8px;
151-
justify-content: center;
152-
}
153-
154-
@media (min-width: 960px) {
155-
.wt-hero-badges-row {
156-
justify-content: flex-start;
157-
}
158-
}
159-
160-
.wt-hero-badges-wrap {
161-
text-align: left;
162-
line-height: 12px;
163-
}
164-
165147
/* Default theme centers hero text on small screens when a hero image exists.
166148
Keep this site left-aligned across breakpoints. */
167149
.VPHero.has-image .container {
168150
text-align: left;
169151
}
170152

171-
.wt-hero-badge {
172-
display: inline-flex;
173-
line-height: 0;
174-
}
175-
176-
.wt-hero-badge img {
177-
height: 20px;
178-
border-radius: 0;
179-
opacity: 0.95;
180-
transition:
181-
opacity 140ms ease,
182-
transform 140ms ease;
183-
}
184-
185-
.wt-hero-badge:hover img {
186-
opacity: 1;
187-
transform: translateY(-1px);
153+
.vp-doc a {
154+
display: inline-block;
188155
}

docs/guide/endpoints.md

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -164,9 +164,15 @@ export const exampleEndpoints = {
164164
response: z.instanceof(Blob),
165165
},
166166
},
167-
'/links': {
167+
'/authorization/{provider}': {
168168
url: {
169-
response: z.string().url(),
169+
$search: z.object({
170+
popup: z.boolean(),
171+
}).optional(),
172+
$path: z.object({
173+
provider: z.enum(['microsoft', 'google']),
174+
}),
175+
response: z.url(),
170176
},
171177
},
172178
'/events': {

docs/guide/methods.md

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -150,25 +150,40 @@ const [err, deletion] = await client.delete('/users/{id}', { id: '123' });
150150

151151
### DOWNLOAD
152152

153-
`download` performs an HTTP `GET` request under the hood, but handles the response as binary and returns a `Blob` instance (it uses `response.blob()` internally instead of JSON parsing).
153+
`download` performs an HTTP `GET` request under the hood, but handles the response as binary and returns a `Blob` instance (it uses `response.blob()` internally instead of JSON parsing). `response` is optional and only accepts Blob.
154154

155155
```ts
156156
const endpoints = {
157+
// Response not strictly needed, but only accepts Blob instances
157158
'/files/{id}/download': {
158159
download: { response: z.instanceof(Blob) },
159160
},
161+
'/files/{id}/download/{type}': {
162+
download: { $path: z.object({ type: z.enum(['pdf', 'png']) }) },
163+
},
160164
} satisfies RequestDefinitions;
161165

162166
const [err, file] = await client.download('/files/{id}/download', { id: 'file-1' });
163167
```
164168

165169
### URL
166170

171+
Structured similarly to other operations, but `response` is optional and only accepts strings.
172+
167173
```ts
168174
const endpoints = {
169-
'/links': { url: { response: z.string().url() } },
175+
// Response not strictly needed, but only accepts string derivatives
176+
'/links': {
177+
url: { response: z.url() }
178+
},
179+
'/links/{integration}': {
180+
url: {
181+
$path: z.object({ integration: z.enum(['slack', 'github']) })
182+
}
183+
},
170184
} satisfies RequestDefinitions;
171185

186+
172187
const [err, link] = await client.url('/links', null);
173188
```
174189

@@ -178,7 +193,7 @@ Use `url()` when you want a validated, parsed URL without making a request, e.g.
178193

179194
#### Why async?
180195

181-
`url()` generates a URL from the endpoint template + params and validates/parses the final result using the endpoint’s `url.response` schema. The “response” schema here is simply used to express that the return type is a string (and lets you attach extra validation like `z.string().url()`).
196+
`url()` generates a URL from the endpoint template + params and validates/parses the final result using the endpoint’s `url.response` schema. The “response” schema here is simply used to express that the return type is a string (and lets you attach extra validation like `z.url()`).
182197

183198
It’s async because Standard Schema validators can be async, so the API is async even though it doesn’t hit the network.
184199

docs/index.md

Lines changed: 10 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -6,30 +6,6 @@ hero:
66
name: WireTyped
77
text: Typed HTTP client for fetch runtimes
88
tagline: Universal fetch-based, typed HTTP client with error-first ergonomics, retries, caching, SSE, and Standard Schema validation.
9-
10-
<!-- All this to avoid a vue direct package -->
11-
<!-- But at least you read this, so that's fun -->
12-
<div class="wt-hero-badges-wrap">
13-
<div class="wt-hero-badges-row">
14-
<a class="wt-hero-badge" href="https://github.com/kasperrt/wiretyped/actions/workflows/ci.yml" target="_blank" rel="noreferrer">
15-
<img src="https://github.com/kasperrt/wiretyped/actions/workflows/ci.yml/badge.svg" alt="CI" loading="lazy" decoding="async" />
16-
</a>
17-
<a class="wt-hero-badge" href="https://codecov.io/gh/kasperrt/wiretyped" target="_blank" rel="noreferrer">
18-
<img src="https://codecov.io/gh/kasperrt/wiretyped/branch/main/graph/badge.svg" alt="Coverage" loading="lazy" decoding="async" />
19-
</a>
20-
<a class="wt-hero-badge" href="https://bundlejs.com/?q=wiretyped@latest" target="_blank" rel="noreferrer">
21-
<img src="https://deno.bundlejs.com/badge?q=wiretyped@latest" alt="minzip" loading="lazy" decoding="async" />
22-
</a>
23-
</div>
24-
<div class="wt-hero-badges-row">
25-
<a class="wt-hero-badge" href="https://www.npmjs.com/package/wiretyped" target="_blank" rel="noreferrer">
26-
<img src="https://img.shields.io/npm/v/wiretyped.svg" alt="npm" loading="lazy" decoding="async" />
27-
</a>
28-
<a class="wt-hero-badge" href="https://jsr.io/@kasperrt/wiretyped" target="_blank" rel="noreferrer">
29-
<img src="https://jsr.io/badges/@kasperrt/wiretyped" alt="JSR" loading="lazy" decoding="async">
30-
</a>
31-
</div>
32-
</div>
339

3410
actions:
3511
- theme: brand
@@ -60,13 +36,20 @@ features:
6036

6137
## What is it?
6238

39+
> Small and easy-to-use fetch based client with runtime validation
40+
41+
42+
[![CI](https://github.com/kasperrt/wiretyped/actions/workflows/ci.yml/badge.svg)](https://github.com/kasperrt/wiretyped/actions/workflows/ci.yml) &nbsp; [![Coverage](https://codecov.io/gh/kasperrt/wiretyped/branch/main/graph/badge.svg)](https://codecov.io/gh/kasperrt/wiretyped) &nbsp; [![minzip](https://deno.bundlejs.com/badge?q=wiretyped@latest)](https://bundlejs.com/?q=wiretyped@latest)
43+
44+
[![npm](https://img.shields.io/npm/v/wiretyped.svg)](https://www.npmjs.com/package/wiretyped) &nbsp; [![JSR](https://jsr.io/badges/@kasperrt/wiretyped)](https://jsr.io/@kasperrt/wiretyped)
45+
6346
WireTyped is a small, composable HTTP client for fetch-based runtimes (browser, Node, Bun, Deno, workers). You define your API as typed endpoint definitions and call it with a consistent, error-first API.
6447

65-
It has a tiny runtime surface area: one dependency, `@standard-schema/spec`, so you can bring your own validator (Zod, Valibot, ArkType, etc.) without dragging in a whole framework or a pile of transitive deps.
48+
It keeps a tiny runtime surface area: one dependency, `@standard-schema/spec`, so you can bring your own validator (Zod, Valibot, ArkType, etc.) without dragging in a whole framework or a pile of transitive deps.
6649

6750
I built it because I got tired of the same three problems: surprise runtime errors, wrapping every request in `try/catch`, and threading schemas + type inference through every call just to keep types strict and consistent. WireTyped centralizes that work in your endpoint definitions, so using the client stays predictable and pretty.
6851

69-
## Quick example
52+
## Basic example
7053

7154
Define your endpoints once, then call them with a consistent `[err, data]` shape with convenient type-narrowing:
7255

@@ -96,6 +79,4 @@ console.log(user.name);
9679

9780
## Okay, wow
9881

99-
This is the part where you pause for a second and go: okay, yeah. This is kinda beautiful. Let’s keep going.
100-
101-
I got you: [`/guide/getting-started`](/guide/getting-started)
82+
If you’re nodding along and want to keep moving, jump into the quickstart: [`/guide/getting-started`](/guide/getting-started)

jsr.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@kasperrt/wiretyped",
3-
"version": "0.3.3-alpha.0",
3+
"version": "0.3.3-alpha.1",
44
"exports": {
55
".": "./src/index.ts"
66
},

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "wiretyped",
3-
"version": "0.3.3-alpha.0",
3+
"version": "0.3.3-alpha.1",
44
"description": "Universal fetch-based HTTP client with robust error handling, retries, caching, SSE, and Standard Schema validation.",
55
"type": "module",
66
"keywords": [

pnpm-workspace.yaml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
onlyBuiltDependencies:
2+
- esbuild
3+
- sharp
4+
- workerd

src/cache/client.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -80,8 +80,7 @@ export class CacheClient {
8080
* @param {Object} data - cache data
8181
*/
8282
#add<T = unknown>(key: string, data: T, ttl: number) {
83-
const expires = Date.now() + ttl;
84-
this.#cache.set(key, { key, data, expires });
83+
this.#cache.set(key, { key, data, expires: Date.now() + ttl });
8584
}
8685

8786
/**
@@ -124,10 +123,12 @@ export class CacheClient {
124123
}
125124

126125
this.#add(key, data, ttl);
126+
this.#pending.delete(key);
127127
return [null, data];
128128
})();
129129

130130
this.#pending.set(key, pending);
131+
return pending;
131132
};
132133

133134
/**
@@ -150,8 +151,7 @@ export class CacheClient {
150151
return pending as SafeWrapAsync<Error, T>;
151152
}
152153

153-
this.#addPendingRequest(key, res, ttl);
154-
return this.#pending.get(key) as SafeWrapAsync<Error, T>;
154+
return this.#addPendingRequest(key, res, ttl);
155155
};
156156

157157
/**

0 commit comments

Comments
 (0)