@vertz/fetch 0.1.0 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/dist/index.d.ts +55 -16
  2. package/dist/index.js +295 -153
  3. package/package.json +5 -2
package/dist/index.d.ts CHANGED
@@ -1,3 +1,6 @@
1
+ import { EntityErrorType, FetchErrorType, Result as Result3 } from "@vertz/errors";
2
+ import { err, isErr, isOk, matchError, ok, unwrap, unwrapOr } from "@vertz/errors";
3
+ import { FetchError as BaseFetchError, Result } from "@vertz/errors";
1
4
  type AuthStrategy = {
2
5
  type: "bearer";
3
6
  token: string | (() => string | Promise<string>);
@@ -47,19 +50,39 @@ interface RequestOptions {
47
50
  body?: unknown;
48
51
  signal?: AbortSignal;
49
52
  }
50
- interface FetchResponse<T> {
53
+ type FetchResponse<T> = Result<{
51
54
  data: T;
52
55
  status: number;
53
56
  headers: Headers;
54
- }
57
+ }, BaseFetchError>;
55
58
  interface StreamingRequestOptions extends RequestOptions {
56
59
  format: StreamingFormat;
57
60
  }
61
+ /** Paginated list response envelope returned by entity list endpoints. */
62
+ interface ListResponse<T> {
63
+ items: T[];
64
+ total: number;
65
+ limit: number;
66
+ nextCursor: string | null;
67
+ hasNextPage: boolean;
68
+ }
58
69
  declare class FetchClient {
59
70
  private readonly config;
60
- private readonly fetchFn;
71
+ /**
72
+ * Custom fetch provided via config, bound to globalThis.
73
+ * When not provided, globalThis.fetch is read at call time — this allows
74
+ * SSR handlers to patch globalThis.fetch for relative URL resolution
75
+ * without the client capturing a stale reference at construction time.
76
+ */
77
+ private readonly customFetchFn;
61
78
  constructor(config: FetchClientConfig);
79
+ private get fetchFn();
62
80
  request<T>(method: string, path: string, options?: RequestOptions): Promise<FetchResponse<T>>;
81
+ get<T>(path: string, options?: RequestOptions): Promise<FetchResponse<T>>;
82
+ post<T>(path: string, body?: unknown, options?: RequestOptions): Promise<FetchResponse<T>>;
83
+ put<T>(path: string, body?: unknown, options?: RequestOptions): Promise<FetchResponse<T>>;
84
+ patch<T>(path: string, body?: unknown, options?: RequestOptions): Promise<FetchResponse<T>>;
85
+ delete<T>(path: string, options?: RequestOptions): Promise<FetchResponse<T>>;
63
86
  requestStream<T>(options: StreamingRequestOptions & {
64
87
  method: string;
65
88
  path: string;
@@ -75,40 +98,56 @@ declare class FetchClient {
75
98
  private applyStrategy;
76
99
  private safeParseJSON;
77
100
  }
78
- declare class FetchError extends Error {
101
+ import { FetchError, Result as Result2 } from "@vertz/errors";
102
+ interface QueryDescriptor<
103
+ T,
104
+ E = FetchError
105
+ > extends PromiseLike<Result2<T, E>> {
106
+ readonly _tag: "QueryDescriptor";
107
+ readonly _key: string;
108
+ readonly _fetch: () => Promise<Result2<T, E>>;
109
+ /** Phantom field to carry the error type through generics. Never set at runtime. */
110
+ readonly _error?: E;
111
+ }
112
+ declare function isQueryDescriptor<
113
+ T,
114
+ E = FetchError
115
+ >(value: unknown): value is QueryDescriptor<T, E>;
116
+ declare function createDescriptor<T>(method: string, path: string, fetchFn: () => Promise<FetchResponse<T>>, query?: Record<string, unknown>): QueryDescriptor<T>;
117
+ declare class FetchError2 extends Error {
79
118
  readonly status: number;
80
119
  readonly body?: unknown;
81
120
  constructor(message: string, status: number, body?: unknown);
82
121
  }
83
- declare class BadRequestError extends FetchError {
122
+ declare class BadRequestError extends FetchError2 {
84
123
  constructor(message: string, body?: unknown);
85
124
  }
86
- declare class UnauthorizedError extends FetchError {
125
+ declare class UnauthorizedError extends FetchError2 {
87
126
  constructor(message: string, body?: unknown);
88
127
  }
89
- declare class ForbiddenError extends FetchError {
128
+ declare class ForbiddenError extends FetchError2 {
90
129
  constructor(message: string, body?: unknown);
91
130
  }
92
- declare class NotFoundError extends FetchError {
131
+ declare class NotFoundError extends FetchError2 {
93
132
  constructor(message: string, body?: unknown);
94
133
  }
95
- declare class ConflictError extends FetchError {
134
+ declare class ConflictError extends FetchError2 {
96
135
  constructor(message: string, body?: unknown);
97
136
  }
98
- declare class GoneError extends FetchError {
137
+ declare class GoneError extends FetchError2 {
99
138
  constructor(message: string, body?: unknown);
100
139
  }
101
- declare class UnprocessableEntityError extends FetchError {
140
+ declare class UnprocessableEntityError extends FetchError2 {
102
141
  constructor(message: string, body?: unknown);
103
142
  }
104
- declare class RateLimitError extends FetchError {
143
+ declare class RateLimitError extends FetchError2 {
105
144
  constructor(message: string, body?: unknown);
106
145
  }
107
- declare class InternalServerError extends FetchError {
146
+ declare class InternalServerError extends FetchError2 {
108
147
  constructor(message: string, body?: unknown);
109
148
  }
110
- declare class ServiceUnavailableError extends FetchError {
149
+ declare class ServiceUnavailableError extends FetchError2 {
111
150
  constructor(message: string, body?: unknown);
112
151
  }
113
- declare function createErrorFromStatus(status: number, message: string, body?: unknown): FetchError;
114
- export { createErrorFromStatus, UnprocessableEntityError, UnauthorizedError, StreamingRequestOptions, StreamingFormat, ServiceUnavailableError, RetryConfig, RequestOptions, RateLimitError, NotFoundError, InternalServerError, HooksConfig, GoneError, ForbiddenError, FetchResponse, FetchError, FetchClientConfig, FetchClient, ConflictError, BadRequestError, AuthStrategy };
152
+ declare function createErrorFromStatus(status: number, message: string, body?: unknown): FetchError2;
153
+ export { unwrapOr, unwrap, ok, matchError, isQueryDescriptor, isOk, isErr, err, createErrorFromStatus, createDescriptor, UnprocessableEntityError, UnauthorizedError, StreamingRequestOptions, StreamingFormat, ServiceUnavailableError, RetryConfig, Result3 as Result, RequestOptions, RateLimitError, QueryDescriptor, NotFoundError, ListResponse, InternalServerError, HooksConfig, GoneError, ForbiddenError, FetchResponse, FetchErrorType, FetchError2 as FetchError, FetchClientConfig, FetchClient, EntityErrorType, ConflictError, BadRequestError, AuthStrategy };
package/dist/index.js CHANGED
@@ -1,162 +1,128 @@
1
- // src/errors.ts
2
- class FetchError extends Error {
3
- status;
4
- body;
5
- constructor(message, status, body) {
6
- super(message);
7
- this.name = "FetchError";
8
- this.status = status;
9
- this.body = body;
10
- }
11
- }
12
-
13
- class BadRequestError extends FetchError {
14
- constructor(message, body) {
15
- super(message, 400, body);
16
- this.name = "BadRequestError";
17
- }
18
- }
19
-
20
- class UnauthorizedError extends FetchError {
21
- constructor(message, body) {
22
- super(message, 401, body);
23
- this.name = "UnauthorizedError";
24
- }
25
- }
26
-
27
- class ForbiddenError extends FetchError {
28
- constructor(message, body) {
29
- super(message, 403, body);
30
- this.name = "ForbiddenError";
31
- }
32
- }
33
-
34
- class NotFoundError extends FetchError {
35
- constructor(message, body) {
36
- super(message, 404, body);
37
- this.name = "NotFoundError";
38
- }
39
- }
40
-
41
- class ConflictError extends FetchError {
42
- constructor(message, body) {
43
- super(message, 409, body);
44
- this.name = "ConflictError";
45
- }
46
- }
47
-
48
- class GoneError extends FetchError {
49
- constructor(message, body) {
50
- super(message, 410, body);
51
- this.name = "GoneError";
52
- }
53
- }
54
-
55
- class UnprocessableEntityError extends FetchError {
56
- constructor(message, body) {
57
- super(message, 422, body);
58
- this.name = "UnprocessableEntityError";
59
- }
60
- }
61
-
62
- class RateLimitError extends FetchError {
63
- constructor(message, body) {
64
- super(message, 429, body);
65
- this.name = "RateLimitError";
66
- }
67
- }
68
-
69
- class InternalServerError extends FetchError {
70
- constructor(message, body) {
71
- super(message, 500, body);
72
- this.name = "InternalServerError";
73
- }
74
- }
75
-
76
- class ServiceUnavailableError extends FetchError {
77
- constructor(message, body) {
78
- super(message, 503, body);
79
- this.name = "ServiceUnavailableError";
80
- }
81
- }
82
- var errorMap = {
83
- 400: BadRequestError,
84
- 401: UnauthorizedError,
85
- 403: ForbiddenError,
86
- 404: NotFoundError,
87
- 409: ConflictError,
88
- 410: GoneError,
89
- 422: UnprocessableEntityError,
90
- 429: RateLimitError,
91
- 500: InternalServerError,
92
- 503: ServiceUnavailableError
93
- };
94
- function createErrorFromStatus(status, message, body) {
95
- const ErrorClass = errorMap[status];
96
- if (ErrorClass) {
97
- return new ErrorClass(message, body);
98
- }
99
- return new FetchError(message, status, body);
100
- }
1
+ // src/index.ts
2
+ import { err as err2, isErr, isOk, matchError, ok as ok3, unwrap, unwrapOr } from "@vertz/errors";
101
3
 
102
4
  // src/client.ts
5
+ import {
6
+ createHttpError,
7
+ err,
8
+ FetchNetworkError,
9
+ FetchTimeoutError,
10
+ FetchValidationError,
11
+ ok,
12
+ ParseError
13
+ } from "@vertz/errors";
103
14
  var DEFAULT_RETRY_ON = [429, 500, 502, 503, 504];
104
15
 
105
16
  class FetchClient {
106
17
  config;
107
- fetchFn;
18
+ customFetchFn;
108
19
  constructor(config) {
109
20
  this.config = config;
110
- this.fetchFn = config.fetch ?? globalThis.fetch;
21
+ this.customFetchFn = config.fetch ? config.fetch.bind(globalThis) : null;
22
+ }
23
+ get fetchFn() {
24
+ return this.customFetchFn ?? globalThis.fetch.bind(globalThis);
111
25
  }
112
26
  async request(method, path, options) {
113
- const retryConfig = this.resolveRetryConfig();
114
- let lastError;
115
- for (let attempt = 0;attempt <= retryConfig.retries; attempt++) {
116
- if (attempt > 0) {
117
- const delay = this.calculateBackoff(attempt, retryConfig);
118
- await this.sleep(delay);
119
- }
120
- const url = this.buildURL(path, options?.query);
121
- const headers = new Headers(this.config.headers);
122
- if (options?.headers) {
123
- for (const [key, value] of Object.entries(options.headers)) {
124
- headers.set(key, value);
27
+ try {
28
+ const retryConfig = this.resolveRetryConfig();
29
+ let lastError;
30
+ for (let attempt = 0;attempt <= retryConfig.retries; attempt++) {
31
+ if (attempt > 0) {
32
+ const delay = this.calculateBackoff(attempt, retryConfig);
33
+ await this.sleep(delay);
125
34
  }
35
+ const url = this.buildURL(path, options?.query);
36
+ const headers = new Headers(this.config.headers);
37
+ if (options?.headers) {
38
+ for (const [key, value] of Object.entries(options.headers)) {
39
+ headers.set(key, value);
40
+ }
41
+ }
42
+ const signal = this.buildSignal(options?.signal);
43
+ const isRelativeUrl = url.startsWith("/");
44
+ const requestUrl = isRelativeUrl ? `http://localhost${url}` : url;
45
+ const serializedBody = options?.body !== undefined ? JSON.stringify(options.body) : undefined;
46
+ const request = new Request(requestUrl, {
47
+ method,
48
+ headers,
49
+ body: serializedBody,
50
+ signal
51
+ });
52
+ if (options?.body !== undefined) {
53
+ request.headers.set("Content-Type", "application/json");
54
+ }
55
+ const authedRequest = await this.applyAuth(request);
56
+ await this.config.hooks?.beforeRequest?.(authedRequest);
57
+ const response = isRelativeUrl ? await this.fetchFn(url, {
58
+ method: authedRequest.method,
59
+ headers: authedRequest.headers,
60
+ body: serializedBody,
61
+ signal: authedRequest.signal
62
+ }) : await this.fetchFn(authedRequest);
63
+ if (!response.ok) {
64
+ const body = await this.safeParseJSON(response);
65
+ let serverCode;
66
+ if (body && typeof body === "object" && "error" in body) {
67
+ const errorObj = body.error;
68
+ if (errorObj && typeof errorObj.code === "string") {
69
+ serverCode = errorObj.code;
70
+ }
71
+ if (errorObj?.code === "ValidationError" && Array.isArray(errorObj.errors)) {
72
+ return err(new FetchValidationError("Validation failed", errorObj.errors));
73
+ }
74
+ }
75
+ const httpError = createHttpError(response.status, response.statusText, serverCode);
76
+ if (attempt < retryConfig.retries && retryConfig.retryOn.includes(response.status)) {
77
+ lastError = httpError;
78
+ await this.config.hooks?.beforeRetry?.(attempt + 1, httpError);
79
+ continue;
80
+ }
81
+ await this.config.hooks?.onError?.(httpError);
82
+ return err(httpError);
83
+ }
84
+ await this.config.hooks?.afterResponse?.(response);
85
+ if (response.status === 204 || response.status === 205) {
86
+ return ok({ data: undefined, status: response.status, headers: response.headers });
87
+ }
88
+ let data;
89
+ try {
90
+ data = await response.json();
91
+ } catch (parseError) {
92
+ return err(new ParseError("", "Failed to parse response JSON", parseError));
93
+ }
94
+ return ok({
95
+ data,
96
+ status: response.status,
97
+ headers: response.headers
98
+ });
126
99
  }
127
- const signal = this.buildSignal(options?.signal);
128
- const request = new Request(url, {
129
- method,
130
- headers,
131
- body: options?.body !== undefined ? JSON.stringify(options.body) : undefined,
132
- signal
133
- });
134
- if (options?.body !== undefined) {
135
- request.headers.set("Content-Type", "application/json");
136
- }
137
- const authedRequest = await this.applyAuth(request);
138
- await this.config.hooks?.beforeRequest?.(authedRequest);
139
- const response = await this.fetchFn(authedRequest);
140
- if (!response.ok) {
141
- const body = await this.safeParseJSON(response);
142
- const error = createErrorFromStatus(response.status, response.statusText, body);
143
- if (attempt < retryConfig.retries && retryConfig.retryOn.includes(response.status)) {
144
- lastError = error;
145
- await this.config.hooks?.beforeRetry?.(attempt + 1, error);
146
- continue;
100
+ return err(lastError ?? new FetchNetworkError("All retries exhausted"));
101
+ } catch (error) {
102
+ if (error instanceof Error && error.name === "AbortError") {
103
+ const abortSignal = error instanceof DOMException ? error.cause : error;
104
+ if (abortSignal instanceof Error && abortSignal.name === "TimeoutError") {
105
+ return err(new FetchTimeoutError);
147
106
  }
148
- await this.config.hooks?.onError?.(error);
149
- throw error;
107
+ return err(new FetchNetworkError("Request aborted"));
150
108
  }
151
- await this.config.hooks?.afterResponse?.(response);
152
- const data = await response.json();
153
- return {
154
- data,
155
- status: response.status,
156
- headers: response.headers
157
- };
109
+ return err(new FetchNetworkError("Network request failed"));
158
110
  }
159
- throw lastError;
111
+ }
112
+ async get(path, options) {
113
+ return this.request("GET", path, options);
114
+ }
115
+ async post(path, body, options) {
116
+ return this.request("POST", path, { ...options, body });
117
+ }
118
+ async put(path, body, options) {
119
+ return this.request("PUT", path, { ...options, body });
120
+ }
121
+ async patch(path, body, options) {
122
+ return this.request("PATCH", path, { ...options, body });
123
+ }
124
+ async delete(path, options) {
125
+ return this.request("DELETE", path, options);
160
126
  }
161
127
  async* requestStream(options) {
162
128
  const url = this.buildURL(options.path, options.query);
@@ -172,20 +138,35 @@ class FetchClient {
172
138
  headers.set("Accept", "application/x-ndjson");
173
139
  }
174
140
  const signal = this.buildSignal(options.signal);
175
- const request = new Request(url, {
141
+ const isRelativeUrl = url.startsWith("/");
142
+ const requestUrl = isRelativeUrl ? `http://localhost${url}` : url;
143
+ const serializedBody = options.body !== undefined ? JSON.stringify(options.body) : undefined;
144
+ const request = new Request(requestUrl, {
176
145
  method: options.method,
177
146
  headers,
178
- body: options.body !== undefined ? JSON.stringify(options.body) : undefined,
147
+ body: serializedBody,
179
148
  signal
180
149
  });
181
150
  const authedRequest = await this.applyAuth(request);
182
151
  await this.config.hooks?.beforeRequest?.(authedRequest);
183
- const response = await this.fetchFn(authedRequest);
152
+ const response = isRelativeUrl ? await this.fetchFn(url, {
153
+ method: authedRequest.method,
154
+ headers: authedRequest.headers,
155
+ body: serializedBody,
156
+ signal: authedRequest.signal
157
+ }) : await this.fetchFn(authedRequest);
184
158
  if (!response.ok) {
185
159
  const body = await this.safeParseJSON(response);
186
- const error = createErrorFromStatus(response.status, response.statusText, body);
187
- await this.config.hooks?.onError?.(error);
188
- throw error;
160
+ let serverCode;
161
+ if (body && typeof body === "object" && "error" in body) {
162
+ const errorObj = body.error;
163
+ if (errorObj && typeof errorObj.code === "string") {
164
+ serverCode = errorObj.code;
165
+ }
166
+ }
167
+ const httpError = createHttpError(response.status, response.statusText, serverCode);
168
+ await this.config.hooks?.onError?.(httpError);
169
+ throw httpError;
189
170
  }
190
171
  if (!response.body) {
191
172
  return;
@@ -290,15 +271,32 @@ class FetchClient {
290
271
  }
291
272
  buildURL(path, query) {
292
273
  const base = this.config.baseURL;
293
- const url = base ? new URL(path, base) : new URL(path);
274
+ const isAbsoluteBase = base && /^https?:\/\//.test(base);
275
+ let urlString;
276
+ if (base && isAbsoluteBase) {
277
+ const relativePath = path.startsWith("/") ? path.slice(1) : path;
278
+ const normalizedBase = base.endsWith("/") ? base : `${base}/`;
279
+ urlString = new URL(relativePath, normalizedBase).toString();
280
+ } else if (base) {
281
+ const normalizedBase = base.endsWith("/") ? base.slice(0, -1) : base;
282
+ const normalizedPath = path.startsWith("/") ? path : `/${path}`;
283
+ urlString = `${normalizedBase}${normalizedPath}`;
284
+ } else {
285
+ urlString = path;
286
+ }
294
287
  if (query) {
288
+ const params = new URLSearchParams;
295
289
  for (const [key, value] of Object.entries(query)) {
296
290
  if (value !== undefined && value !== null) {
297
- url.searchParams.set(key, String(value));
291
+ params.set(key, String(value));
298
292
  }
299
293
  }
294
+ const qs = params.toString();
295
+ if (qs) {
296
+ urlString += `${urlString.includes("?") ? "&" : "?"}${qs}`;
297
+ }
300
298
  }
301
- return url.toString();
299
+ return urlString;
302
300
  }
303
301
  async applyAuth(request) {
304
302
  const strategies = this.config.authStrategies;
@@ -346,8 +344,152 @@ class FetchClient {
346
344
  }
347
345
  }
348
346
  }
347
+ // src/descriptor.ts
348
+ import { ok as ok2 } from "@vertz/errors";
349
+ function isQueryDescriptor(value) {
350
+ return value !== null && typeof value === "object" && "_tag" in value && value._tag === "QueryDescriptor";
351
+ }
352
+ function createDescriptor(method, path, fetchFn, query) {
353
+ const key = `${method}:${path}${serializeQuery(query)}`;
354
+ const fetchResult = async () => {
355
+ const response = await fetchFn();
356
+ if (!response.ok)
357
+ return response;
358
+ return ok2(response.data.data);
359
+ };
360
+ return {
361
+ _tag: "QueryDescriptor",
362
+ _key: key,
363
+ _fetch: fetchResult,
364
+ then(onFulfilled, onRejected) {
365
+ return fetchResult().then(onFulfilled, onRejected);
366
+ }
367
+ };
368
+ }
369
+ function serializeQuery(query) {
370
+ if (!query)
371
+ return "";
372
+ const params = new URLSearchParams;
373
+ for (const key of Object.keys(query).sort()) {
374
+ const value = query[key];
375
+ if (value !== undefined && value !== null) {
376
+ params.set(key, String(value));
377
+ }
378
+ }
379
+ const str = params.toString();
380
+ return str ? `?${str}` : "";
381
+ }
382
+ // src/errors.ts
383
+ class FetchError extends Error {
384
+ status;
385
+ body;
386
+ constructor(message, status, body) {
387
+ super(message);
388
+ this.name = "FetchError";
389
+ this.status = status;
390
+ this.body = body;
391
+ }
392
+ }
393
+
394
+ class BadRequestError extends FetchError {
395
+ constructor(message, body) {
396
+ super(message, 400, body);
397
+ this.name = "BadRequestError";
398
+ }
399
+ }
400
+
401
+ class UnauthorizedError extends FetchError {
402
+ constructor(message, body) {
403
+ super(message, 401, body);
404
+ this.name = "UnauthorizedError";
405
+ }
406
+ }
407
+
408
+ class ForbiddenError extends FetchError {
409
+ constructor(message, body) {
410
+ super(message, 403, body);
411
+ this.name = "ForbiddenError";
412
+ }
413
+ }
414
+
415
+ class NotFoundError extends FetchError {
416
+ constructor(message, body) {
417
+ super(message, 404, body);
418
+ this.name = "NotFoundError";
419
+ }
420
+ }
421
+
422
+ class ConflictError extends FetchError {
423
+ constructor(message, body) {
424
+ super(message, 409, body);
425
+ this.name = "ConflictError";
426
+ }
427
+ }
428
+
429
+ class GoneError extends FetchError {
430
+ constructor(message, body) {
431
+ super(message, 410, body);
432
+ this.name = "GoneError";
433
+ }
434
+ }
435
+
436
+ class UnprocessableEntityError extends FetchError {
437
+ constructor(message, body) {
438
+ super(message, 422, body);
439
+ this.name = "UnprocessableEntityError";
440
+ }
441
+ }
442
+
443
+ class RateLimitError extends FetchError {
444
+ constructor(message, body) {
445
+ super(message, 429, body);
446
+ this.name = "RateLimitError";
447
+ }
448
+ }
449
+
450
+ class InternalServerError extends FetchError {
451
+ constructor(message, body) {
452
+ super(message, 500, body);
453
+ this.name = "InternalServerError";
454
+ }
455
+ }
456
+
457
+ class ServiceUnavailableError extends FetchError {
458
+ constructor(message, body) {
459
+ super(message, 503, body);
460
+ this.name = "ServiceUnavailableError";
461
+ }
462
+ }
463
+ var errorMap = {
464
+ 400: BadRequestError,
465
+ 401: UnauthorizedError,
466
+ 403: ForbiddenError,
467
+ 404: NotFoundError,
468
+ 409: ConflictError,
469
+ 410: GoneError,
470
+ 422: UnprocessableEntityError,
471
+ 429: RateLimitError,
472
+ 500: InternalServerError,
473
+ 503: ServiceUnavailableError
474
+ };
475
+ function createErrorFromStatus(status, message, body) {
476
+ const ErrorClass = errorMap[status];
477
+ if (ErrorClass) {
478
+ return new ErrorClass(message, body);
479
+ }
480
+ return new FetchError(message, status, body);
481
+ }
349
482
  export {
483
+ unwrapOr,
484
+ unwrap,
485
+ ok3 as ok,
486
+ matchError,
487
+ isQueryDescriptor,
488
+ isOk,
489
+ isErr,
490
+ err2 as err,
350
491
  createErrorFromStatus,
492
+ createDescriptor,
351
493
  UnprocessableEntityError,
352
494
  UnauthorizedError,
353
495
  ServiceUnavailableError,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vertz/fetch",
3
- "version": "0.1.0",
3
+ "version": "0.2.1",
4
4
  "type": "module",
5
5
  "license": "MIT",
6
6
  "description": "Type-safe HTTP client for Vertz",
@@ -24,6 +24,9 @@
24
24
  "files": [
25
25
  "dist"
26
26
  ],
27
+ "dependencies": {
28
+ "@vertz/errors": "0.2.1"
29
+ },
27
30
  "scripts": {
28
31
  "build": "bunup",
29
32
  "test": "vitest run",
@@ -31,7 +34,7 @@
31
34
  "typecheck": "tsc --noEmit"
32
35
  },
33
36
  "devDependencies": {
34
- "@types/node": "^22.0.0",
37
+ "@types/node": "^25.3.1",
35
38
  "@vitest/coverage-v8": "^4.0.18",
36
39
  "bunup": "latest",
37
40
  "typescript": "^5.7.0",