@anmetric/neta 1.1.0 → 1.1.2

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 (2) hide show
  1. package/README.md +122 -87
  2. package/package.json +5 -1
package/README.md CHANGED
@@ -16,18 +16,20 @@ npm install @anmetric/neta
16
16
  ## Quick Start
17
17
 
18
18
  ```js
19
- import neta from 'neta';
19
+ import neta from "@anmetric/neta";
20
20
 
21
21
  // GET with JSON parsing
22
- const data = await neta.get('https://api.example.com/users').json();
22
+ const data = await neta.get("https://api.example.com/users").json();
23
23
 
24
24
  // POST with JSON body
25
- const user = await neta.post('https://api.example.com/users', {
26
- json: { name: 'John', email: 'john@example.com' },
27
- }).json();
25
+ const user = await neta
26
+ .post("https://api.example.com/users", {
27
+ json: { name: "John", email: "john@example.com" },
28
+ })
29
+ .json();
28
30
 
29
31
  // Direct callable
30
- const res = await neta('https://api.example.com/users', { method: 'get' });
32
+ const res = await neta("https://api.example.com/users", { method: "get" });
31
33
  ```
32
34
 
33
35
  ## API
@@ -53,9 +55,11 @@ Type: `unknown`
53
55
  JSON body. Automatically stringified and sets `Content-Type: application/json`.
54
56
 
55
57
  ```js
56
- const data = await neta.post('https://api.example.com/items', {
57
- json: { title: 'New Item' },
58
- }).json();
58
+ const data = await neta
59
+ .post("https://api.example.com/items", {
60
+ json: { title: "New Item" },
61
+ })
62
+ .json();
59
63
  ```
60
64
 
61
65
  ##### searchParams
@@ -65,9 +69,11 @@ Type: `string | object | URLSearchParams | Array<[string, string]>`
65
69
  Query parameters appended to the URL.
66
70
 
67
71
  ```js
68
- const data = await neta.get('https://api.example.com/search', {
69
- searchParams: { q: 'hello', page: 2 },
70
- }).json();
72
+ const data = await neta
73
+ .get("https://api.example.com/search", {
74
+ searchParams: { q: "hello", page: 2 },
75
+ })
76
+ .json();
71
77
  // => GET https://api.example.com/search?q=hello&page=2
72
78
  ```
73
79
 
@@ -80,9 +86,9 @@ Type: `string | URL`
80
86
  Prefix prepended to the input URL. Useful for API base paths.
81
87
 
82
88
  ```js
83
- const api = neta.create({ prefix: 'https://api.example.com/v2' });
89
+ const api = neta.create({ prefix: "https://api.example.com/v2" });
84
90
 
85
- await api.get('users').json();
91
+ await api.get("users").json();
86
92
  // => GET https://api.example.com/v2/users
87
93
  ```
88
94
 
@@ -93,12 +99,12 @@ Type: `string | URL`
93
99
  Base URL for resolving relative inputs using standard [URL resolution](https://developer.mozilla.org/en-US/docs/Web/API/URL/URL).
94
100
 
95
101
  ```js
96
- const api = neta.create({ baseUrl: 'https://api.example.com/v2/' });
102
+ const api = neta.create({ baseUrl: "https://api.example.com/v2/" });
97
103
 
98
- await api.get('users').json();
104
+ await api.get("users").json();
99
105
  // => GET https://api.example.com/v2/users
100
106
 
101
- await api.get('../v1/legacy').json();
107
+ await api.get("../v1/legacy").json();
102
108
  // => GET https://api.example.com/v1/legacy
103
109
  ```
104
110
 
@@ -117,7 +123,7 @@ Default: `false`
117
123
  Total timeout across all retries in milliseconds.
118
124
 
119
125
  ```js
120
- await neta.get('https://api.example.com/slow', {
126
+ await neta.get("https://api.example.com/slow", {
121
127
  timeout: 5000,
122
128
  totalTimeout: 30000,
123
129
  retry: 5,
@@ -139,7 +145,7 @@ await neta.get(url, { retry: 3 });
139
145
  await neta.get(url, {
140
146
  retry: {
141
147
  limit: 3,
142
- methods: ['get', 'put', 'head', 'delete', 'options'],
148
+ methods: ["get", "put", "head", "delete", "options"],
143
149
  statusCodes: [408, 413, 429, 500, 502, 503, 504],
144
150
  afterStatusCodes: [413, 429, 503],
145
151
  maxRetryAfter: Infinity,
@@ -274,11 +280,13 @@ Type: `(text: string, context: { request, response }) => unknown`
274
280
  Custom JSON parser. Useful for reviving dates, BigInts, etc.
275
281
 
276
282
  ```js
277
- import LosslessJSON from 'lossless-json';
283
+ import LosslessJSON from "lossless-json";
278
284
 
279
- const data = await neta.get(url, {
280
- parseJson: (text) => LosslessJSON.parse(text),
281
- }).json();
285
+ const data = await neta
286
+ .get(url, {
287
+ parseJson: (text) => LosslessJSON.parse(text),
288
+ })
289
+ .json();
282
290
  ```
283
291
 
284
292
  ##### stringifyJson
@@ -288,7 +296,7 @@ Type: `(value: unknown) => string`
288
296
  Custom JSON serializer for the `json` option.
289
297
 
290
298
  ```js
291
- import LosslessJSON from 'lossless-json';
299
+ import LosslessJSON from "lossless-json";
292
300
 
293
301
  await neta.post(url, {
294
302
  json: data,
@@ -303,15 +311,17 @@ Type: `string`
303
311
  Convenience option to set the `Authorization: Bearer <token>` header. If an `Authorization` header is already set explicitly, `bearerToken` will not override it.
304
312
 
305
313
  ```js
306
- const data = await neta.get('https://api.example.com/me', {
307
- bearerToken: 'abc123',
308
- }).json();
314
+ const data = await neta
315
+ .get("https://api.example.com/me", {
316
+ bearerToken: "abc123",
317
+ })
318
+ .json();
309
319
  // Sets header: Authorization: Bearer abc123
310
320
 
311
321
  // Works with create/extend
312
322
  const api = neta.create({
313
- prefix: 'https://api.example.com',
314
- bearerToken: 'abc123',
323
+ prefix: "https://api.example.com",
324
+ bearerToken: "abc123",
315
325
  });
316
326
  ```
317
327
 
@@ -324,11 +334,16 @@ Arbitrary data passed through to hooks. Not sent with the request.
324
334
 
325
335
  ```js
326
336
  await neta.get(url, {
327
- context: { token: 'abc123' },
337
+ context: { token: "abc123" },
328
338
  hooks: {
329
- init: [(options) => {
330
- options.headers = { ...options.headers, Authorization: `Bearer ${options.context.token}` };
331
- }],
339
+ init: [
340
+ (options) => {
341
+ options.headers = {
342
+ ...options.headers,
343
+ Authorization: `Bearer ${options.context.token}`,
344
+ };
345
+ },
346
+ ],
332
347
  },
333
348
  });
334
349
  ```
@@ -340,7 +355,7 @@ Type: `typeof globalThis.fetch`
340
355
  Custom fetch implementation.
341
356
 
342
357
  ```js
343
- import { fetch } from 'undici';
358
+ import { fetch } from "undici";
344
359
 
345
360
  const api = neta.create({ fetch });
346
361
  ```
@@ -352,9 +367,11 @@ Type: `(progress: { percent, transferredBytes, totalBytes }) => void`
352
367
  Download progress callback. Requires `ReadableStream` support.
353
368
 
354
369
  ```js
355
- await neta.get('https://example.com/large-file', {
370
+ await neta.get("https://example.com/large-file", {
356
371
  onDownloadProgress: ({ percent, transferredBytes, totalBytes }) => {
357
- console.log(`${Math.round(percent * 100)}% (${transferredBytes}/${totalBytes})`);
372
+ console.log(
373
+ `${Math.round(percent * 100)}% (${transferredBytes}/${totalBytes})`,
374
+ );
358
375
  },
359
376
  });
360
377
  ```
@@ -394,12 +411,14 @@ const form = await neta.get(url).formData();
394
411
  Parse response as JSON. Optionally validate against a [Standard Schema](https://github.com/standard-schema/standard-schema):
395
412
 
396
413
  ```js
397
- import { z } from 'zod';
398
-
399
- const user = await neta.get('/user/1').json(z.object({
400
- id: z.number(),
401
- name: z.string(),
402
- }));
414
+ import { z } from "zod";
415
+
416
+ const user = await neta.get("/user/1").json(
417
+ z.object({
418
+ id: z.number(),
419
+ name: z.string(),
420
+ }),
421
+ );
403
422
  // Throws SchemaValidationError if validation fails
404
423
  ```
405
424
 
@@ -411,13 +430,13 @@ Create a new instance with default options:
411
430
 
412
431
  ```js
413
432
  const api = neta.create({
414
- prefix: 'https://api.example.com',
415
- bearerToken: 'my-token',
433
+ prefix: "https://api.example.com",
434
+ bearerToken: "my-token",
416
435
  timeout: 30000,
417
436
  retry: 3,
418
437
  });
419
438
 
420
- const data = await api.get('users').json();
439
+ const data = await api.get("users").json();
421
440
  ```
422
441
 
423
442
  #### neta.extend(defaults?)
@@ -425,8 +444,8 @@ const data = await api.get('users').json();
425
444
  Alias for `neta.create()`. Creates a new instance by extending existing defaults:
426
445
 
427
446
  ```js
428
- const api = neta.create({ prefix: 'https://api.example.com' });
429
- const authApi = api.extend({ bearerToken: 'my-token' });
447
+ const api = neta.create({ prefix: "https://api.example.com" });
448
+ const authApi = api.extend({ bearerToken: "my-token" });
430
449
  ```
431
450
 
432
451
  ## Hooks
@@ -442,9 +461,14 @@ Called synchronously before anything else. Can mutate options directly.
442
461
  ```js
443
462
  neta.create({
444
463
  hooks: {
445
- init: [(options) => {
446
- options.headers = { ...options.headers, 'X-Request-Id': crypto.randomUUID() };
447
- }],
464
+ init: [
465
+ (options) => {
466
+ options.headers = {
467
+ ...options.headers,
468
+ "X-Request-Id": crypto.randomUUID(),
469
+ };
470
+ },
471
+ ],
448
472
  },
449
473
  });
450
474
  ```
@@ -458,9 +482,11 @@ Called before each request. Return a `Request` to replace it, a `Response` to sh
458
482
  ```js
459
483
  neta.create({
460
484
  hooks: {
461
- beforeRequest: [({ request }) => {
462
- console.log(`${request.method} ${request.url}`);
463
- }],
485
+ beforeRequest: [
486
+ ({ request }) => {
487
+ console.log(`${request.method} ${request.url}`);
488
+ },
489
+ ],
464
490
  },
465
491
  });
466
492
  ```
@@ -474,16 +500,21 @@ Called after a successful response. Return a `Response` to replace it, or `neta.
474
500
  ```js
475
501
  const api = neta.create({
476
502
  hooks: {
477
- afterResponse: [async ({ request, response }) => {
478
- if (response.status === 401) {
479
- const token = await refreshToken();
480
- return neta.retry({
481
- request: new Request(request, {
482
- headers: { ...Object.fromEntries(request.headers), Authorization: `Bearer ${token}` },
483
- }),
484
- });
485
- }
486
- }],
503
+ afterResponse: [
504
+ async ({ request, response }) => {
505
+ if (response.status === 401) {
506
+ const token = await refreshToken();
507
+ return neta.retry({
508
+ request: new Request(request, {
509
+ headers: {
510
+ ...Object.fromEntries(request.headers),
511
+ Authorization: `Bearer ${token}`,
512
+ },
513
+ }),
514
+ });
515
+ }
516
+ },
517
+ ],
487
518
  },
488
519
  });
489
520
  ```
@@ -497,12 +528,14 @@ Called before an error is thrown. Return an `Error` to replace it.
497
528
  ```js
498
529
  neta.create({
499
530
  hooks: {
500
- beforeError: [({ error }) => {
501
- if (error instanceof HTTPError) {
502
- error.message = `API Error: ${error.response.status}`;
503
- }
504
- return error;
505
- }],
531
+ beforeError: [
532
+ ({ error }) => {
533
+ if (error instanceof HTTPError) {
534
+ error.message = `API Error: ${error.response.status}`;
535
+ }
536
+ return error;
537
+ },
538
+ ],
506
539
  },
507
540
  });
508
541
  ```
@@ -519,14 +552,16 @@ Called before each retry attempt. Return:
519
552
  - nothing — proceed normally
520
553
 
521
554
  ```js
522
- import { stop } from 'neta';
555
+ import { stop } from "neta";
523
556
 
524
557
  neta.create({
525
558
  hooks: {
526
- beforeRetry: [({ error, retryCount }) => {
527
- console.log(`Retry #${retryCount}: ${error.message}`);
528
- if (retryCount > 3) return stop;
529
- }],
559
+ beforeRetry: [
560
+ ({ error, retryCount }) => {
561
+ console.log(`Retry #${retryCount}: ${error.message}`);
562
+ if (retryCount > 3) return stop;
563
+ },
564
+ ],
530
565
  },
531
566
  });
532
567
  ```
@@ -538,15 +573,15 @@ neta.create({
538
573
  Thrown for non-2xx responses (when `throwHttpErrors` is true).
539
574
 
540
575
  ```js
541
- import { HTTPError } from 'neta';
576
+ import { HTTPError } from "neta";
542
577
 
543
578
  try {
544
- await neta.get('https://api.example.com/missing');
579
+ await neta.get("https://api.example.com/missing");
545
580
  } catch (error) {
546
581
  if (error instanceof HTTPError) {
547
582
  console.log(error.response.status); // 404
548
- console.log(error.data); // Auto-parsed response body
549
- console.log(error.request); // The Request object
583
+ console.log(error.data); // Auto-parsed response body
584
+ console.log(error.request); // The Request object
550
585
  }
551
586
  }
552
587
  ```
@@ -556,13 +591,13 @@ try {
556
591
  Thrown when a request exceeds the `timeout` or `totalTimeout`.
557
592
 
558
593
  ```js
559
- import { TimeoutError } from 'neta';
594
+ import { TimeoutError } from "neta";
560
595
 
561
596
  try {
562
597
  await neta.get(url, { timeout: 1000 });
563
598
  } catch (error) {
564
599
  if (error instanceof TimeoutError) {
565
- console.log('Request timed out:', error.request.url);
600
+ console.log("Request timed out:", error.request.url);
566
601
  }
567
602
  }
568
603
  ```
@@ -572,14 +607,14 @@ try {
572
607
  Thrown on network failures (DNS, connection refused, etc.).
573
608
 
574
609
  ```js
575
- import { NetworkError } from 'neta';
610
+ import { NetworkError } from "neta";
576
611
 
577
612
  try {
578
- await neta.get('https://nonexistent.invalid');
613
+ await neta.get("https://nonexistent.invalid");
579
614
  } catch (error) {
580
615
  if (error instanceof NetworkError) {
581
- console.log('Network error:', error.message);
582
- console.log('Cause:', error.cause);
616
+ console.log("Network error:", error.message);
617
+ console.log("Cause:", error.cause);
583
618
  }
584
619
  }
585
620
  ```
@@ -589,13 +624,13 @@ try {
589
624
  Thrown when JSON response fails schema validation.
590
625
 
591
626
  ```js
592
- import { SchemaValidationError } from 'neta';
627
+ import { SchemaValidationError } from "neta";
593
628
 
594
629
  try {
595
630
  await neta.get(url).json(mySchema);
596
631
  } catch (error) {
597
632
  if (error instanceof SchemaValidationError) {
598
- console.log('Validation issues:', error.issues);
633
+ console.log("Validation issues:", error.issues);
599
634
  }
600
635
  }
601
636
  ```
@@ -610,7 +645,7 @@ interface User {
610
645
  name: string;
611
646
  }
612
647
 
613
- const user = await neta.get('https://api.example.com/user/1').json<User>();
648
+ const user = await neta.get("https://api.example.com/user/1").json<User>();
614
649
  // user is typed as User
615
650
  ```
616
651
 
package/package.json CHANGED
@@ -1,7 +1,11 @@
1
1
  {
2
2
  "name": "@anmetric/neta",
3
- "version": "1.1.0",
3
+ "version": "1.1.2",
4
4
  "description": "Tiny, elegant HTTP client built on fetch for browser and Node.js",
5
+ "repository": {
6
+ "type": "git",
7
+ "url": "https://github.com/anmetrics/neta.git"
8
+ },
5
9
  "type": "module",
6
10
  "exports": {
7
11
  ".": {