@tmlmobilidade/utils 20260526.2143.45 → 20260527.1749.0

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.
@@ -0,0 +1,23 @@
1
+ import { HttpResponse } from './response.js';
2
+ /**
3
+ * Fetches data from a URL with configurable HTTP method, body, headers, and options.
4
+ * @param url - The URL to fetch from
5
+ * @param method - The HTTP method to use (DELETE, GET, POST, PUT). Defaults to GET.
6
+ * @param body - Optional request body data
7
+ * @param headers - Optional request headers
8
+ * @param options - Optional fetch options (excluding body, headers, method)
9
+ * @returns Promise resolving to HttpResponse containing data, error and status
10
+ * @example
11
+ * ```ts
12
+ * // GET request
13
+ * const response = await fetchData<User>('/api/users/123');
14
+ *
15
+ * // POST request with body
16
+ * const response = await fetchData<User>(
17
+ * '/api/users',
18
+ * 'POST',
19
+ * { name: 'John', email: 'john@example.com' }
20
+ * );
21
+ * ```
22
+ */
23
+ export declare function fetchData<T>(url: string, method?: 'DELETE' | 'GET' | 'POST' | 'PUT', body?: unknown, headers?: Record<string, string>, options?: Omit<RequestInit, 'body' | 'headers' | 'method'>, retries?: number): Promise<HttpResponse<T>>;
@@ -0,0 +1,77 @@
1
+ /* * */
2
+ import { HttpResponse } from './response.js';
3
+ /**
4
+ * Fetches data from a URL with configurable HTTP method, body, headers, and options.
5
+ * @param url - The URL to fetch from
6
+ * @param method - The HTTP method to use (DELETE, GET, POST, PUT). Defaults to GET.
7
+ * @param body - Optional request body data
8
+ * @param headers - Optional request headers
9
+ * @param options - Optional fetch options (excluding body, headers, method)
10
+ * @returns Promise resolving to HttpResponse containing data, error and status
11
+ * @example
12
+ * ```ts
13
+ * // GET request
14
+ * const response = await fetchData<User>('/api/users/123');
15
+ *
16
+ * // POST request with body
17
+ * const response = await fetchData<User>(
18
+ * '/api/users',
19
+ * 'POST',
20
+ * { name: 'John', email: 'john@example.com' }
21
+ * );
22
+ * ```
23
+ */
24
+ export async function fetchData(url, method = 'GET', body, headers = {}, options = {}, retries = 3) {
25
+ let attempt = 0;
26
+ let lastError;
27
+ while (attempt <= retries) {
28
+ try {
29
+ const response = await fetch(url, {
30
+ body: body ? JSON.stringify(body) : undefined,
31
+ credentials: 'include',
32
+ headers: {
33
+ ...(method === 'GET' || method === 'DELETE' || 'Content-Type' in headers
34
+ ? {}
35
+ : { 'Content-Type': 'application/json' }),
36
+ ...headers,
37
+ },
38
+ method,
39
+ ...options,
40
+ });
41
+ const data = (await response.json());
42
+ if (!response.ok || data.error) {
43
+ // Don't retry for 4xx (client) errors
44
+ if (response.status >= 400 && response.status < 500) {
45
+ return new HttpResponse({
46
+ data: null,
47
+ error: data.error,
48
+ statusCode: response.status,
49
+ });
50
+ }
51
+ throw new Error(`HTTP ${response.status} - ${data.error ?? 'Unknown error'}`);
52
+ }
53
+ return new HttpResponse({
54
+ data: data.data,
55
+ error: null,
56
+ statusCode: response.status,
57
+ });
58
+ }
59
+ catch (error) {
60
+ lastError = error;
61
+ attempt++;
62
+ // Stop retrying if we've reached the limit
63
+ if (attempt > retries)
64
+ break;
65
+ // Wait before retrying (exponential backoff: 0.5s, 1s, 2s, etc.)
66
+ const delay = 500 * Math.pow(2, attempt - 1);
67
+ await new Promise(resolve => setTimeout(resolve, delay));
68
+ }
69
+ }
70
+ return new HttpResponse({
71
+ data: null,
72
+ error: lastError instanceof Error
73
+ ? `${lastError.message} - ${lastError.cause ?? ''}`
74
+ : 'Network error',
75
+ statusCode: 500,
76
+ });
77
+ }
@@ -0,0 +1,17 @@
1
+ export type WithPagination<T> = T & {
2
+ pagination: {
3
+ limit: number;
4
+ page: number;
5
+ total: number;
6
+ };
7
+ };
8
+ /**
9
+ * Fetches data from a URL using the SWR fetcher function without authentication.
10
+ * @param url - The URL to fetch from
11
+ * @returns Promise resolving to the fetched data
12
+ * @example
13
+ * ```ts
14
+ * const data = await standardSwrFetcher('/api/users/123');
15
+ * ```
16
+ */
17
+ export declare const standardSwrFetcher: <T>(url: string) => Promise<T>;
@@ -0,0 +1,18 @@
1
+ /* * */
2
+ import { HttpException } from '@tmlmobilidade/consts';
3
+ /**
4
+ * Fetches data from a URL using the SWR fetcher function without authentication.
5
+ * @param url - The URL to fetch from
6
+ * @returns Promise resolving to the fetched data
7
+ * @example
8
+ * ```ts
9
+ * const data = await standardSwrFetcher('/api/users/123');
10
+ * ```
11
+ */
12
+ export const standardSwrFetcher = async (url) => {
13
+ const res = await fetch(url, { credentials: 'omit' });
14
+ if (!res.ok) {
15
+ throw new HttpException(res.status, res.statusText);
16
+ }
17
+ return res.json();
18
+ };
@@ -0,0 +1,5 @@
1
+ export * from './fetch-data.js';
2
+ export * from './http.js';
3
+ export * from './multipart.js';
4
+ export * from './response.js';
5
+ export * from './swr.js';
@@ -0,0 +1,5 @@
1
+ export * from './fetch-data.js';
2
+ export * from './http.js';
3
+ export * from './multipart.js';
4
+ export * from './response.js';
5
+ export * from './swr.js';
@@ -0,0 +1,26 @@
1
+ import { HttpResponse } from './response.js';
2
+ /**
3
+ * Sends a multipart form data request to a URL.
4
+ * @param url - The URL to send the request to
5
+ * @param formData - The FormData object containing the multipart form data
6
+ * @returns Promise resolving to HttpResponse containing data, error and status
7
+ * @example
8
+ * ```ts
9
+ * const formData = new FormData();
10
+ * formData.append('file', fileBlob);
11
+ * formData.append('name', 'profile.jpg');
12
+ * const response = await multipartFetch('/api/upload', formData);
13
+ * ```
14
+ */
15
+ export declare function multipartFetch<T>(url: string, formData: FormData): Promise<HttpResponse<T>>;
16
+ /**
17
+ * Uploads a file to a URL using multipart form data.
18
+ * @param url - The URL to send the request to
19
+ * @param file - The file to upload
20
+ * @returns Promise resolving to HttpResponse containing data, error and status
21
+ * @example
22
+ * ```ts
23
+ * const response = await uploadFile('/api/upload', file);
24
+ * ```
25
+ */
26
+ export declare function uploadFile<T>(url: string, file: File): Promise<HttpResponse<T>>;
@@ -0,0 +1,59 @@
1
+ /* * */
2
+ import { HttpResponse } from './response.js';
3
+ /**
4
+ * Sends a multipart form data request to a URL.
5
+ * @param url - The URL to send the request to
6
+ * @param formData - The FormData object containing the multipart form data
7
+ * @returns Promise resolving to HttpResponse containing data, error and status
8
+ * @example
9
+ * ```ts
10
+ * const formData = new FormData();
11
+ * formData.append('file', fileBlob);
12
+ * formData.append('name', 'profile.jpg');
13
+ * const response = await multipartFetch('/api/upload', formData);
14
+ * ```
15
+ */
16
+ export async function multipartFetch(url, formData) {
17
+ try {
18
+ const response = await fetch(url, {
19
+ body: formData,
20
+ credentials: 'include',
21
+ method: 'POST',
22
+ });
23
+ const data = await response.json();
24
+ if (!response.ok || data.error) {
25
+ return new HttpResponse({
26
+ data: null,
27
+ error: data.error,
28
+ statusCode: response.status,
29
+ });
30
+ }
31
+ return new HttpResponse({
32
+ data: data.data,
33
+ error: null,
34
+ statusCode: response.status,
35
+ });
36
+ }
37
+ catch (error) {
38
+ return new HttpResponse({
39
+ data: null,
40
+ error: error instanceof Error ? error.message : 'Network error',
41
+ statusCode: 500,
42
+ });
43
+ }
44
+ }
45
+ /**
46
+ * Uploads a file to a URL using multipart form data.
47
+ * @param url - The URL to send the request to
48
+ * @param file - The file to upload
49
+ * @returns Promise resolving to HttpResponse containing data, error and status
50
+ * @example
51
+ * ```ts
52
+ * const response = await uploadFile('/api/upload', file);
53
+ * ```
54
+ */
55
+ export async function uploadFile(url, file) {
56
+ const formData = new FormData();
57
+ formData.append('file', file, file.name);
58
+ return await multipartFetch(url, formData);
59
+ }
@@ -0,0 +1,11 @@
1
+ export declare class HttpResponse<T> {
2
+ readonly data: null | T;
3
+ readonly error: null | string;
4
+ readonly isOk?: () => boolean;
5
+ readonly statusCode: number;
6
+ constructor({ data, error, statusCode }: {
7
+ data: null | T;
8
+ error: null | string;
9
+ statusCode: number;
10
+ });
11
+ }
@@ -0,0 +1,13 @@
1
+ /* * */
2
+ export class HttpResponse {
3
+ data;
4
+ error;
5
+ isOk;
6
+ statusCode;
7
+ constructor({ data, error, statusCode }) {
8
+ this.data = data;
9
+ this.error = error;
10
+ this.statusCode = statusCode;
11
+ this.isOk = () => statusCode >= 200 && statusCode < 300;
12
+ }
13
+ }
@@ -0,0 +1,28 @@
1
+ export interface SwrFetcherOptions {
2
+ credentials?: RequestInit['credentials'];
3
+ url: string;
4
+ }
5
+ /**
6
+ * Fetches data from a URL using the SWR fetcher function.
7
+ * @param urlOrOptions The URL to fetch from or the options object.
8
+ * @returns Promise resolving to the fetched data
9
+ * @example
10
+ * ```ts
11
+ * // Fetch data from a URL
12
+ * const data = await swrFetcher('/api/users/123');
13
+ *
14
+ * // Fetch data from a URL with options
15
+ * const data = await swrFetcher({ url: '/api/users/123', credentials: 'omit' });
16
+ * ```
17
+ */
18
+ export declare function swrFetcher<T>(urlOrOptions: string | SwrFetcherOptions): Promise<T>;
19
+ /**
20
+ * Fetches data from a URL using the SWR fetcher function.
21
+ * @param urlOrOptions The URL to fetch from or the options object.
22
+ * @returns Promise resolving to the fetched data
23
+ * @deprecated Use `swrFetcher` with an options object instead.
24
+ * ```ts
25
+ * const data = await swrFetcher({ url: '/api/users/123', credentials: 'omit' });
26
+ * ```
27
+ */
28
+ export declare function unauthenticatedSwrFetcher<T>(url: string): Promise<T>;
@@ -0,0 +1,47 @@
1
+ /* * */
2
+ import { HttpException } from '@tmlmobilidade/consts';
3
+ /**
4
+ * Fetches data from a URL using the SWR fetcher function.
5
+ * @param urlOrOptions The URL to fetch from or the options object.
6
+ * @returns Promise resolving to the fetched data
7
+ * @example
8
+ * ```ts
9
+ * // Fetch data from a URL
10
+ * const data = await swrFetcher('/api/users/123');
11
+ *
12
+ * // Fetch data from a URL with options
13
+ * const data = await swrFetcher({ url: '/api/users/123', credentials: 'omit' });
14
+ * ```
15
+ */
16
+ export async function swrFetcher(urlOrOptions) {
17
+ //
18
+ //
19
+ // Extract the URL either from the string directly
20
+ // or from the options object
21
+ const urlValue = typeof urlOrOptions === 'string' ? urlOrOptions : urlOrOptions.url;
22
+ //
23
+ // Extract the credentials option from the options object
24
+ // or use the default value. By default, it uses 'include' credentials.
25
+ const credentialsValue = typeof urlOrOptions === 'string' ? 'include' : urlOrOptions.credentials ?? 'include';
26
+ //
27
+ // Perform the fetch operation
28
+ const res = await fetch(urlValue, { credentials: credentialsValue });
29
+ const data = await res.json();
30
+ if (!res.ok) {
31
+ throw new HttpException(res.status, data.error ?? 'An error occurred');
32
+ }
33
+ return data.data;
34
+ }
35
+ ;
36
+ /**
37
+ * Fetches data from a URL using the SWR fetcher function.
38
+ * @param urlOrOptions The URL to fetch from or the options object.
39
+ * @returns Promise resolving to the fetched data
40
+ * @deprecated Use `swrFetcher` with an options object instead.
41
+ * ```ts
42
+ * const data = await swrFetcher({ url: '/api/users/123', credentials: 'omit' });
43
+ * ```
44
+ */
45
+ export async function unauthenticatedSwrFetcher(url) {
46
+ return swrFetcher({ credentials: 'omit', url: url });
47
+ }
package/dist/index.d.ts CHANGED
@@ -2,7 +2,7 @@ export * from './batching/index.js';
2
2
  export * from './caching/index.js';
3
3
  export * from './generic/index.js';
4
4
  export * from './get-public-trip-id.js';
5
- export * from './http.js';
5
+ export * from './http/index.js';
6
6
  export * from './numbers/index.js';
7
7
  export * from './objects/index.js';
8
8
  export * from './permissions.js';
package/dist/index.js CHANGED
@@ -2,7 +2,7 @@ export * from './batching/index.js';
2
2
  export * from './caching/index.js';
3
3
  export * from './generic/index.js';
4
4
  export * from './get-public-trip-id.js';
5
- export * from './http.js';
5
+ export * from './http/index.js';
6
6
  export * from './numbers/index.js';
7
7
  export * from './objects/index.js';
8
8
  export * from './permissions.js';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tmlmobilidade/utils",
3
- "version": "20260526.2143.45",
3
+ "version": "20260527.1749.0",
4
4
  "author": {
5
5
  "email": "iso@tmlmobilidade.pt",
6
6
  "name": "TML-ISO"
package/dist/http.d.ts DELETED
@@ -1,95 +0,0 @@
1
- export declare class HttpResponse<T> {
2
- readonly data: null | T;
3
- readonly error: null | string;
4
- readonly isOk?: () => boolean;
5
- readonly statusCode: number;
6
- constructor({ data, error, statusCode }: {
7
- data: null | T;
8
- error: null | string;
9
- statusCode: number;
10
- });
11
- }
12
- export type WithPagination<T> = T & {
13
- pagination: {
14
- limit: number;
15
- page: number;
16
- total: number;
17
- };
18
- };
19
- /**
20
- * Fetches data from a URL with configurable HTTP method, body, headers, and options.
21
- * @param url - The URL to fetch from
22
- * @param method - The HTTP method to use (DELETE, GET, POST, PUT). Defaults to GET.
23
- * @param body - Optional request body data
24
- * @param headers - Optional request headers
25
- * @param options - Optional fetch options (excluding body, headers, method)
26
- * @returns Promise resolving to HttpResponse containing data, error and status
27
- * @example
28
- * ```ts
29
- * // GET request
30
- * const response = await fetchData<User>('/api/users/123');
31
- *
32
- * // POST request with body
33
- * const response = await fetchData<User>(
34
- * '/api/users',
35
- * 'POST',
36
- * { name: 'John', email: 'john@example.com' }
37
- * );
38
- * ```
39
- */
40
- export declare function fetchData<T>(url: string, method?: 'DELETE' | 'GET' | 'POST' | 'PUT', body?: unknown, headers?: Record<string, string>, options?: Omit<RequestInit, 'body' | 'headers' | 'method'>, retries?: number): Promise<HttpResponse<T>>;
41
- /**
42
- * Sends a multipart form data request to a URL.
43
- * @param url - The URL to send the request to
44
- * @param formData - The FormData object containing the multipart form data
45
- * @returns Promise resolving to HttpResponse containing data, error and status
46
- * @example
47
- * ```ts
48
- * const formData = new FormData();
49
- * formData.append('file', fileBlob);
50
- * formData.append('name', 'profile.jpg');
51
- * const response = await multipartFetch('/api/upload', formData);
52
- * ```
53
- */
54
- export declare function multipartFetch<T>(url: string, formData: FormData): Promise<HttpResponse<T>>;
55
- /**
56
- * Uploads a file to a URL using multipart form data.
57
- * @param url - The URL to send the request to
58
- * @param file - The file to upload
59
- * @returns Promise resolving to HttpResponse containing data, error and status
60
- * @example
61
- * ```ts
62
- * const response = await uploadFile('/api/upload', file);
63
- * ```
64
- */
65
- export declare function uploadFile<T>(url: string, file: File): Promise<HttpResponse<T>>;
66
- /**
67
- * Fetches data from a URL using the SWR fetcher function.
68
- * @param url - The URL to fetch from
69
- * @returns Promise resolving to the fetched data
70
- * @example
71
- * ```ts
72
- * const data = await swrFetcher('/api/users/123');
73
- * ```
74
- */
75
- export declare const swrFetcher: <T>(url: string) => Promise<T>;
76
- /**
77
- * Fetches data from a URL using the SWR fetcher function.
78
- * @param url - The URL to fetch from
79
- * @returns Promise resolving to the fetched data
80
- * @example
81
- * ```ts
82
- * const data = await swrFetcher('/api/users/123');
83
- * ```
84
- */
85
- export declare const unauthenticatedSwrFetcher: <T>(url: string) => Promise<T>;
86
- /**
87
- * Fetches data from a URL using the SWR fetcher function without authentication.
88
- * @param url - The URL to fetch from
89
- * @returns Promise resolving to the fetched data
90
- * @example
91
- * ```ts
92
- * const data = await standardSwrFetcher('/api/users/123');
93
- * ```
94
- */
95
- export declare const standardSwrFetcher: <T>(url: string) => Promise<T>;
package/dist/http.js DELETED
@@ -1,195 +0,0 @@
1
- import { HttpException } from '@tmlmobilidade/consts';
2
- export class HttpResponse {
3
- data;
4
- error;
5
- isOk;
6
- statusCode;
7
- constructor({ data, error, statusCode }) {
8
- this.data = data;
9
- this.error = error;
10
- this.statusCode = statusCode;
11
- this.isOk = () => statusCode >= 200 && statusCode < 300;
12
- }
13
- }
14
- /**
15
- * Fetches data from a URL with configurable HTTP method, body, headers, and options.
16
- * @param url - The URL to fetch from
17
- * @param method - The HTTP method to use (DELETE, GET, POST, PUT). Defaults to GET.
18
- * @param body - Optional request body data
19
- * @param headers - Optional request headers
20
- * @param options - Optional fetch options (excluding body, headers, method)
21
- * @returns Promise resolving to HttpResponse containing data, error and status
22
- * @example
23
- * ```ts
24
- * // GET request
25
- * const response = await fetchData<User>('/api/users/123');
26
- *
27
- * // POST request with body
28
- * const response = await fetchData<User>(
29
- * '/api/users',
30
- * 'POST',
31
- * { name: 'John', email: 'john@example.com' }
32
- * );
33
- * ```
34
- */
35
- export async function fetchData(url, method = 'GET', body, headers = {}, options = {}, retries = 3) {
36
- let attempt = 0;
37
- let lastError;
38
- while (attempt <= retries) {
39
- try {
40
- const response = await fetch(url, {
41
- body: body ? JSON.stringify(body) : undefined,
42
- credentials: 'include',
43
- headers: {
44
- ...(method === 'GET' || method === 'DELETE' || 'Content-Type' in headers
45
- ? {}
46
- : { 'Content-Type': 'application/json' }),
47
- ...headers,
48
- },
49
- method,
50
- ...options,
51
- });
52
- const data = (await response.json());
53
- if (!response.ok || data.error) {
54
- // Don't retry for 4xx (client) errors
55
- if (response.status >= 400 && response.status < 500) {
56
- return new HttpResponse({
57
- data: null,
58
- error: data.error,
59
- statusCode: response.status,
60
- });
61
- }
62
- throw new Error(`HTTP ${response.status} - ${data.error ?? 'Unknown error'}`);
63
- }
64
- return new HttpResponse({
65
- data: data.data,
66
- error: null,
67
- statusCode: response.status,
68
- });
69
- }
70
- catch (error) {
71
- lastError = error;
72
- attempt++;
73
- // Stop retrying if we've reached the limit
74
- if (attempt > retries)
75
- break;
76
- // Wait before retrying (exponential backoff: 0.5s, 1s, 2s, etc.)
77
- const delay = 500 * Math.pow(2, attempt - 1);
78
- await new Promise(resolve => setTimeout(resolve, delay));
79
- }
80
- }
81
- return new HttpResponse({
82
- data: null,
83
- error: lastError instanceof Error
84
- ? `${lastError.message} - ${lastError.cause ?? ''}`
85
- : 'Network error',
86
- statusCode: 500,
87
- });
88
- }
89
- /**
90
- * Sends a multipart form data request to a URL.
91
- * @param url - The URL to send the request to
92
- * @param formData - The FormData object containing the multipart form data
93
- * @returns Promise resolving to HttpResponse containing data, error and status
94
- * @example
95
- * ```ts
96
- * const formData = new FormData();
97
- * formData.append('file', fileBlob);
98
- * formData.append('name', 'profile.jpg');
99
- * const response = await multipartFetch('/api/upload', formData);
100
- * ```
101
- */
102
- export async function multipartFetch(url, formData) {
103
- try {
104
- const response = await fetch(url, {
105
- body: formData,
106
- credentials: 'include',
107
- method: 'POST',
108
- });
109
- const data = await response.json();
110
- if (!response.ok || data.error) {
111
- return new HttpResponse({
112
- data: null,
113
- error: data.error,
114
- statusCode: response.status,
115
- });
116
- }
117
- return new HttpResponse({
118
- data: data.data,
119
- error: null,
120
- statusCode: response.status,
121
- });
122
- }
123
- catch (error) {
124
- return new HttpResponse({
125
- data: null,
126
- error: error instanceof Error ? error.message : 'Network error',
127
- statusCode: 500,
128
- });
129
- }
130
- }
131
- /**
132
- * Uploads a file to a URL using multipart form data.
133
- * @param url - The URL to send the request to
134
- * @param file - The file to upload
135
- * @returns Promise resolving to HttpResponse containing data, error and status
136
- * @example
137
- * ```ts
138
- * const response = await uploadFile('/api/upload', file);
139
- * ```
140
- */
141
- export async function uploadFile(url, file) {
142
- const formData = new FormData();
143
- formData.append('file', file, file.name);
144
- return await multipartFetch(url, formData);
145
- }
146
- /**
147
- * Fetches data from a URL using the SWR fetcher function.
148
- * @param url - The URL to fetch from
149
- * @returns Promise resolving to the fetched data
150
- * @example
151
- * ```ts
152
- * const data = await swrFetcher('/api/users/123');
153
- * ```
154
- */
155
- export const swrFetcher = async (url) => {
156
- const res = await fetch(url, { credentials: 'include' });
157
- const data = await res.json();
158
- if (!res.ok) {
159
- throw new HttpException(res.status, data.error ?? 'An error occurred');
160
- }
161
- return data.data;
162
- };
163
- /**
164
- * Fetches data from a URL using the SWR fetcher function.
165
- * @param url - The URL to fetch from
166
- * @returns Promise resolving to the fetched data
167
- * @example
168
- * ```ts
169
- * const data = await swrFetcher('/api/users/123');
170
- * ```
171
- */
172
- export const unauthenticatedSwrFetcher = async (url) => {
173
- const res = await fetch(url, { credentials: 'omit' });
174
- const data = await res.json();
175
- if (!res.ok) {
176
- throw new HttpException(res.status, data.error ?? 'An error occurred');
177
- }
178
- return data.data;
179
- };
180
- /**
181
- * Fetches data from a URL using the SWR fetcher function without authentication.
182
- * @param url - The URL to fetch from
183
- * @returns Promise resolving to the fetched data
184
- * @example
185
- * ```ts
186
- * const data = await standardSwrFetcher('/api/users/123');
187
- * ```
188
- */
189
- export const standardSwrFetcher = async (url) => {
190
- const res = await fetch(url, { credentials: 'omit' });
191
- if (!res.ok) {
192
- throw new HttpException(res.status, res.statusText);
193
- }
194
- return res.json();
195
- };