@redseat/api 0.0.11 → 0.0.12

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.
package/client.md CHANGED
@@ -1,288 +1,318 @@
1
- # RedseatClient
2
-
3
- The `RedseatClient` class is the low-level HTTP client that handles all communication with Redseat servers. It provides automatic token management, local server detection, and request/response interceptors.
4
-
5
- ## Overview
6
-
7
- `RedseatClient` wraps Axios and adds:
8
- - Automatic token refresh before expiration
9
- - Local server detection (for development)
10
- - 401 error handling with automatic retry
11
- - Request/response interceptors for authentication
12
-
13
- ## Constructor
14
-
15
- ```typescript
16
- new RedseatClient(options: ClientOptions)
17
- ```
18
-
19
- ### ClientOptions Interface
20
-
21
- ```typescript
22
- interface ClientOptions {
23
- server: IServer;
24
- getIdToken: () => Promise<string>;
25
- refreshThreshold?: number; // milliseconds before expiration to refresh (default: 5 minutes)
26
- }
27
- ```
28
-
29
- **Parameters:**
30
- - `server`: Server configuration object with `id`, `url`, and optional `port`
31
- - `getIdToken`: Async function that returns the current ID token from your auth provider
32
- - `refreshThreshold`: Optional. Milliseconds before token expiration to trigger refresh (default: 300000 = 5 minutes)
33
-
34
- **Example:**
35
- ```typescript
36
- const client = new RedseatClient({
37
- server: {
38
- id: 'server-123',
39
- url: 'example.com',
40
- port: 443
41
- },
42
- getIdToken: async () => {
43
- // Get ID token from your auth provider (Firebase, Auth0, etc.)
44
- return await getCurrentUserToken();
45
- },
46
- refreshThreshold: 5 * 60 * 1000 // 5 minutes
47
- });
48
- ```
49
-
50
- ## Features
51
-
52
- ### Automatic Token Refresh
53
-
54
- The client automatically refreshes tokens before they expire. The refresh happens:
55
- - Before each request if the token is expired or expiring soon
56
- - When a 401 error is received (with automatic retry)
57
-
58
- ### Local Server Detection
59
-
60
- The client automatically detects if a local development server is available by checking `local.{server.url}`. If detected, it uses the local URL instead of the production URL.
61
-
62
- ### Request Interceptor
63
-
64
- All requests are intercepted to:
65
- 1. Check if token needs refresh
66
- 2. Add `Authorization: Bearer {token}` header
67
-
68
- ### Response Interceptor
69
-
70
- All responses are intercepted to:
71
- 1. Handle 401 errors by refreshing token and retrying the request
72
- 2. Prevent infinite retry loops with `_retry` flag
73
-
74
- ## Methods
75
-
76
- ### `get<T>(url: string, config?: AxiosRequestConfig)`
77
-
78
- Performs a GET request.
79
-
80
- **Parameters:**
81
- - `url`: The endpoint URL (relative to base URL)
82
- - `config`: Optional Axios request configuration
83
-
84
- **Returns:** `Promise<AxiosResponse<T>>`
85
-
86
- **Example:**
87
- ```typescript
88
- const response = await client.get<IFile[]>('/libraries/123/medias');
89
- const medias = response.data;
90
- ```
91
-
92
- ### `post<T>(url: string, data?: unknown, config?: AxiosRequestConfig)`
93
-
94
- Performs a POST request.
95
-
96
- **Parameters:**
97
- - `url`: The endpoint URL
98
- - `data`: Request body data
99
- - `config`: Optional Axios request configuration
100
-
101
- **Returns:** `Promise<AxiosResponse<T>>`
102
-
103
- **Example:**
104
- ```typescript
105
- const response = await client.post<ITag>('/libraries/123/tags', {
106
- name: 'Vacation'
107
- });
108
- const tag = response.data;
109
- ```
110
-
111
- ### `put<T>(url: string, data?: unknown, config?: AxiosRequestConfig)`
112
-
113
- Performs a PUT request.
114
-
115
- **Parameters:**
116
- - `url`: The endpoint URL
117
- - `data`: Request body data
118
- - `config`: Optional Axios request configuration
119
-
120
- **Returns:** `Promise<AxiosResponse<T>>`
121
-
122
- **Example:**
123
- ```typescript
124
- const formData = new FormData();
125
- formData.append('file', fileBlob);
126
- const response = await client.put('/libraries/123/medias/transfert', formData);
127
- ```
128
-
129
- ### `patch<T>(url: string, data?: unknown, config?: AxiosRequestConfig)`
130
-
131
- Performs a PATCH request.
132
-
133
- **Parameters:**
134
- - `url`: The endpoint URL
135
- - `data`: Request body data
136
- - `config`: Optional Axios request configuration
137
-
138
- **Returns:** `Promise<AxiosResponse<T>>`
139
-
140
- **Example:**
141
- ```typescript
142
- const response = await client.patch<ITag>('/libraries/123/tags/tag-id', {
143
- rename: 'New Tag Name'
144
- });
145
- const updatedTag = response.data;
146
- ```
147
-
148
- ### `delete<T>(url: string, config?: AxiosRequestConfig)`
149
-
150
- Performs a DELETE request.
151
-
152
- **Parameters:**
153
- - `url`: The endpoint URL
154
- - `config`: Optional Axios request configuration (can include `data` for request body)
155
-
156
- **Returns:** `Promise<AxiosResponse<T>>`
157
-
158
- **Example:**
159
- ```typescript
160
- // Simple delete
161
- await client.delete('/libraries/123/tags/tag-id');
162
-
163
- // Delete with body
164
- await client.delete('/libraries/123/medias', {
165
- data: { ids: ['media-1', 'media-2'] }
166
- });
167
- ```
168
-
169
- ### `request<T>(method: Method, url: string, data?: unknown, config?: AxiosRequestConfig)`
170
-
171
- Performs a custom HTTP request.
172
-
173
- **Parameters:**
174
- - `method`: HTTP method ('GET', 'POST', 'PUT', 'PATCH', 'DELETE', etc.)
175
- - `url`: The endpoint URL
176
- - `data`: Request body data
177
- - `config`: Optional Axios request configuration
178
-
179
- **Returns:** `Promise<AxiosResponse<T>>`
180
-
181
- **Example:**
182
- ```typescript
183
- const response = await client.request<IFile>(
184
- 'GET',
185
- '/libraries/123/medias/media-id',
186
- undefined,
187
- { responseType: 'blob' }
188
- );
189
- ```
190
-
191
- ### `setToken(token: string | IToken)`
192
-
193
- Manually set the authentication token. Useful when you already have a valid token.
194
-
195
- **Parameters:**
196
- - `token`: Either a token string or an `IToken` object with `token` and `expires` properties
197
-
198
- **Example:**
199
- ```typescript
200
- // Set as string (will be treated as expiring soon)
201
- client.setToken('your-token-string');
202
-
203
- // Set as IToken object with expiration
204
- client.setToken({
205
- token: 'your-token-string',
206
- expires: Date.now() + 3600000 // 1 hour from now
207
- });
208
- ```
209
-
210
- ## Error Handling
211
-
212
- The client automatically handles:
213
- - **401 Unauthorized**: Refreshes token and retries the request once
214
- - **Token expiration**: Refreshes token before making requests
215
- - **Network errors**: Passes through to caller
216
-
217
- **Example error handling:**
218
- ```typescript
219
- try {
220
- const response = await client.get('/libraries/123/medias');
221
- } catch (error) {
222
- if (error.response?.status === 401) {
223
- // Token refresh failed or invalid credentials
224
- console.error('Authentication failed');
225
- } else if (error.response?.status === 404) {
226
- // Resource not found
227
- console.error('Resource not found');
228
- } else {
229
- // Other error
230
- console.error('Request failed:', error.message);
231
- }
232
- }
233
- ```
234
-
235
- ## Usage with Response Types
236
-
237
- The client supports specifying response types for binary data:
238
-
239
- ```typescript
240
- // Get as stream
241
- const stream = await client.get('/libraries/123/medias/media-id', {
242
- responseType: 'stream'
243
- });
244
-
245
- // Get as ArrayBuffer
246
- const buffer = await client.get('/libraries/123/medias/media-id', {
247
- responseType: 'arraybuffer'
248
- });
249
-
250
- // Get as Blob
251
- const blob = await client.get('/libraries/123/medias/media-id', {
252
- responseType: 'blob'
253
- });
254
- ```
255
-
256
- ## Progress Tracking
257
-
258
- For file uploads, you can track progress:
259
-
260
- ```typescript
261
- const formData = new FormData();
262
- formData.append('file', fileBlob);
263
-
264
- await client.post('/libraries/123/medias', formData, {
265
- onUploadProgress: (progressEvent) => {
266
- if (progressEvent.total) {
267
- const percent = (progressEvent.loaded / progressEvent.total) * 100;
268
- console.log(`Upload progress: ${percent}%`);
269
- }
270
- }
271
- });
272
- ```
273
-
274
- ## Internal Methods (Private)
275
-
276
- The following methods are used internally and should not be called directly:
277
- - `refreshToken()` - Refreshes the authentication token
278
- - `ensureValidToken()` - Ensures token is valid before requests
279
- - `isTokenExpiredOrExpiringSoon()` - Checks if token needs refresh
280
- - `detectLocalUrl()` - Detects local development server
281
- - `getRegularServerUrl()` - Gets production server URL
282
-
283
- ## See Also
284
-
285
- - [ServerApi Documentation](server.md) - Uses RedseatClient for server operations
286
- - [LibraryApi Documentation](libraries.md) - Uses RedseatClient for library operations
287
- - [README](README.md) - Package overview
288
-
1
+ # RedseatClient
2
+
3
+ The `RedseatClient` class is the low-level HTTP client that handles all communication with Redseat servers. It provides automatic token management, local server detection, and request/response interceptors.
4
+
5
+ ## Overview
6
+
7
+ `RedseatClient` wraps Axios and adds:
8
+ - Automatic token refresh before expiration
9
+ - Local server detection (for development)
10
+ - 401 error handling with automatic retry
11
+ - Request/response interceptors for authentication
12
+
13
+ ## Constructor
14
+
15
+ ```typescript
16
+ new RedseatClient(options: ClientOptions)
17
+ ```
18
+
19
+ ### ClientOptions Interface
20
+
21
+ ```typescript
22
+ interface ClientOptions {
23
+ server: IServer;
24
+ getIdToken: () => Promise<string>;
25
+ refreshThreshold?: number; // milliseconds before expiration to refresh (default: 5 minutes)
26
+ }
27
+ ```
28
+
29
+ **Parameters:**
30
+ - `server`: Server configuration object with `id`, `url`, and optional `port`
31
+ - `getIdToken`: Async function that returns the current ID token from your auth provider
32
+ - `refreshThreshold`: Optional. Milliseconds before token expiration to trigger refresh (default: 300000 = 5 minutes)
33
+
34
+ **Example:**
35
+ ```typescript
36
+ const client = new RedseatClient({
37
+ server: {
38
+ id: 'server-123',
39
+ url: 'example.com',
40
+ port: 443
41
+ },
42
+ getIdToken: async () => {
43
+ // Get ID token from your auth provider (Firebase, Auth0, etc.)
44
+ return await getCurrentUserToken();
45
+ },
46
+ refreshThreshold: 5 * 60 * 1000 // 5 minutes
47
+ });
48
+ ```
49
+
50
+ ## Features
51
+
52
+ ### Automatic Token Refresh
53
+
54
+ The client automatically refreshes tokens before they expire. The refresh happens:
55
+ - Before each request if the token is expired or expiring soon
56
+ - When a 401 error is received (with automatic retry)
57
+
58
+ ### Local Server Detection
59
+
60
+ The client automatically detects if a local development server is available by checking `local.{server.url}`. If detected, it uses the local URL instead of the production URL.
61
+
62
+ ### Request Interceptor
63
+
64
+ All requests are intercepted to:
65
+ 1. Check if token needs refresh
66
+ 2. Add `Authorization: Bearer {token}` header
67
+
68
+ ### Response Interceptor
69
+
70
+ All responses are intercepted to:
71
+ 1. Handle 401 errors by refreshing token and retrying the request
72
+ 2. Prevent infinite retry loops with `_retry` flag
73
+
74
+ ## Methods
75
+
76
+ ### `get<T>(url: string, config?: AxiosRequestConfig)`
77
+
78
+ Performs a GET request.
79
+
80
+ **Parameters:**
81
+ - `url`: The endpoint URL (relative to base URL)
82
+ - `config`: Optional Axios request configuration
83
+
84
+ **Returns:** `Promise<AxiosResponse<T>>`
85
+
86
+ **Example:**
87
+ ```typescript
88
+ const response = await client.get<IFile[]>('/libraries/123/medias');
89
+ const medias = response.data;
90
+ ```
91
+
92
+ ### `post<T>(url: string, data?: unknown, config?: AxiosRequestConfig)`
93
+
94
+ Performs a POST request.
95
+
96
+ **Parameters:**
97
+ - `url`: The endpoint URL
98
+ - `data`: Request body data
99
+ - `config`: Optional Axios request configuration
100
+
101
+ **Returns:** `Promise<AxiosResponse<T>>`
102
+
103
+ **Example:**
104
+ ```typescript
105
+ const response = await client.post<ITag>('/libraries/123/tags', {
106
+ name: 'Vacation'
107
+ });
108
+ const tag = response.data;
109
+ ```
110
+
111
+ ### `postForm<T>(url: string, data?: unknown, config?: AxiosRequestConfig)`
112
+
113
+ Performs a POST request with FormData. This method is specifically designed for multipart/form-data uploads and provides better compatibility with React Native environments compared to using `post` with FormData.
114
+
115
+ **Parameters:**
116
+ - `url`: The endpoint URL
117
+ - `data`: FormData object or data to send as form data
118
+ - `config`: Optional Axios request configuration (supports `onUploadProgress` for progress tracking)
119
+
120
+ **Returns:** `Promise<AxiosResponse<T>>`
121
+
122
+ **Example:**
123
+ ```typescript
124
+ const formData = new FormData();
125
+ formData.append('info', JSON.stringify({ name: 'photo.jpg', size: 1024 }));
126
+ formData.append('file', fileBlob, 'photo.jpg');
127
+
128
+ const response = await client.postForm<IFile>('/libraries/123/medias', formData, {
129
+ onUploadProgress: (progressEvent) => {
130
+ if (progressEvent.total) {
131
+ const percent = (progressEvent.loaded / progressEvent.total) * 100;
132
+ console.log(`Upload progress: ${percent}%`);
133
+ }
134
+ }
135
+ });
136
+ const uploadedFile = response.data;
137
+ ```
138
+
139
+ **Note:** Use `postForm` instead of `post` when sending FormData, especially in React Native applications, as it properly handles multipart/form-data headers and encoding.
140
+
141
+ ### `put<T>(url: string, data?: unknown, config?: AxiosRequestConfig)`
142
+
143
+ Performs a PUT request.
144
+
145
+ **Parameters:**
146
+ - `url`: The endpoint URL
147
+ - `data`: Request body data
148
+ - `config`: Optional Axios request configuration
149
+
150
+ **Returns:** `Promise<AxiosResponse<T>>`
151
+
152
+ **Example:**
153
+ ```typescript
154
+ const formData = new FormData();
155
+ formData.append('file', fileBlob);
156
+ const response = await client.put('/libraries/123/medias/transfert', formData);
157
+ ```
158
+
159
+ ### `patch<T>(url: string, data?: unknown, config?: AxiosRequestConfig)`
160
+
161
+ Performs a PATCH request.
162
+
163
+ **Parameters:**
164
+ - `url`: The endpoint URL
165
+ - `data`: Request body data
166
+ - `config`: Optional Axios request configuration
167
+
168
+ **Returns:** `Promise<AxiosResponse<T>>`
169
+
170
+ **Example:**
171
+ ```typescript
172
+ const response = await client.patch<ITag>('/libraries/123/tags/tag-id', {
173
+ rename: 'New Tag Name'
174
+ });
175
+ const updatedTag = response.data;
176
+ ```
177
+
178
+ ### `delete<T>(url: string, config?: AxiosRequestConfig)`
179
+
180
+ Performs a DELETE request.
181
+
182
+ **Parameters:**
183
+ - `url`: The endpoint URL
184
+ - `config`: Optional Axios request configuration (can include `data` for request body)
185
+
186
+ **Returns:** `Promise<AxiosResponse<T>>`
187
+
188
+ **Example:**
189
+ ```typescript
190
+ // Simple delete
191
+ await client.delete('/libraries/123/tags/tag-id');
192
+
193
+ // Delete with body
194
+ await client.delete('/libraries/123/medias', {
195
+ data: { ids: ['media-1', 'media-2'] }
196
+ });
197
+ ```
198
+
199
+ ### `request<T>(method: Method, url: string, data?: unknown, config?: AxiosRequestConfig)`
200
+
201
+ Performs a custom HTTP request.
202
+
203
+ **Parameters:**
204
+ - `method`: HTTP method ('GET', 'POST', 'PUT', 'PATCH', 'DELETE', etc.)
205
+ - `url`: The endpoint URL
206
+ - `data`: Request body data
207
+ - `config`: Optional Axios request configuration
208
+
209
+ **Returns:** `Promise<AxiosResponse<T>>`
210
+
211
+ **Example:**
212
+ ```typescript
213
+ const response = await client.request<IFile>(
214
+ 'GET',
215
+ '/libraries/123/medias/media-id',
216
+ undefined,
217
+ { responseType: 'blob' }
218
+ );
219
+ ```
220
+
221
+ ### `setToken(token: string | IToken)`
222
+
223
+ Manually set the authentication token. Useful when you already have a valid token.
224
+
225
+ **Parameters:**
226
+ - `token`: Either a token string or an `IToken` object with `token` and `expires` properties
227
+
228
+ **Example:**
229
+ ```typescript
230
+ // Set as string (will be treated as expiring soon)
231
+ client.setToken('your-token-string');
232
+
233
+ // Set as IToken object with expiration
234
+ client.setToken({
235
+ token: 'your-token-string',
236
+ expires: Date.now() + 3600000 // 1 hour from now
237
+ });
238
+ ```
239
+
240
+ ## Error Handling
241
+
242
+ The client automatically handles:
243
+ - **401 Unauthorized**: Refreshes token and retries the request once
244
+ - **Token expiration**: Refreshes token before making requests
245
+ - **Network errors**: Passes through to caller
246
+
247
+ **Example error handling:**
248
+ ```typescript
249
+ try {
250
+ const response = await client.get('/libraries/123/medias');
251
+ } catch (error) {
252
+ if (error.response?.status === 401) {
253
+ // Token refresh failed or invalid credentials
254
+ console.error('Authentication failed');
255
+ } else if (error.response?.status === 404) {
256
+ // Resource not found
257
+ console.error('Resource not found');
258
+ } else {
259
+ // Other error
260
+ console.error('Request failed:', error.message);
261
+ }
262
+ }
263
+ ```
264
+
265
+ ## Usage with Response Types
266
+
267
+ The client supports specifying response types for binary data:
268
+
269
+ ```typescript
270
+ // Get as stream
271
+ const stream = await client.get('/libraries/123/medias/media-id', {
272
+ responseType: 'stream'
273
+ });
274
+
275
+ // Get as ArrayBuffer
276
+ const buffer = await client.get('/libraries/123/medias/media-id', {
277
+ responseType: 'arraybuffer'
278
+ });
279
+
280
+ // Get as Blob
281
+ const blob = await client.get('/libraries/123/medias/media-id', {
282
+ responseType: 'blob'
283
+ });
284
+ ```
285
+
286
+ ## Progress Tracking
287
+
288
+ For file uploads, you can track progress. Use `postForm` for FormData uploads (recommended for React Native compatibility):
289
+
290
+ ```typescript
291
+ const formData = new FormData();
292
+ formData.append('file', fileBlob);
293
+
294
+ await client.postForm('/libraries/123/medias', formData, {
295
+ onUploadProgress: (progressEvent) => {
296
+ if (progressEvent.total) {
297
+ const percent = (progressEvent.loaded / progressEvent.total) * 100;
298
+ console.log(`Upload progress: ${percent}%`);
299
+ }
300
+ }
301
+ });
302
+ ```
303
+
304
+ ## Internal Methods (Private)
305
+
306
+ The following methods are used internally and should not be called directly:
307
+ - `refreshToken()` - Refreshes the authentication token
308
+ - `ensureValidToken()` - Ensures token is valid before requests
309
+ - `isTokenExpiredOrExpiringSoon()` - Checks if token needs refresh
310
+ - `detectLocalUrl()` - Detects local development server
311
+ - `getRegularServerUrl()` - Gets production server URL
312
+
313
+ ## See Also
314
+
315
+ - [ServerApi Documentation](server.md) - Uses RedseatClient for server operations
316
+ - [LibraryApi Documentation](libraries.md) - Uses RedseatClient for library operations
317
+ - [README](README.md) - Package overview
318
+
package/dist/client.d.ts CHANGED
@@ -25,6 +25,7 @@ export declare class RedseatClient {
25
25
  setToken(token: string | IToken): void;
26
26
  get<T = unknown>(url: string, config?: AxiosRequestConfig): Promise<import("axios").AxiosResponse<T, any>>;
27
27
  post<T = unknown>(url: string, data?: unknown, config?: AxiosRequestConfig): Promise<import("axios").AxiosResponse<T, any>>;
28
+ postForm<T = unknown>(url: string, data?: unknown, config?: AxiosRequestConfig): Promise<import("axios").AxiosResponse<T, any>>;
28
29
  put<T = unknown>(url: string, data?: unknown, config?: AxiosRequestConfig): Promise<import("axios").AxiosResponse<T, any>>;
29
30
  patch<T = unknown>(url: string, data?: unknown, config?: AxiosRequestConfig): Promise<import("axios").AxiosResponse<T, any>>;
30
31
  delete<T = unknown>(url: string, config?: AxiosRequestConfig): Promise<import("axios").AxiosResponse<T, any>>;
package/dist/client.js CHANGED
@@ -137,6 +137,9 @@ export class RedseatClient {
137
137
  async post(url, data, config) {
138
138
  return this.axios.post(url, data, config);
139
139
  }
140
+ async postForm(url, data, config) {
141
+ return this.axios.postForm(url, data, config);
142
+ }
140
143
  async put(url, data, config) {
141
144
  return this.axios.put(url, data, config);
142
145
  }
package/dist/library.d.ts CHANGED
@@ -60,6 +60,9 @@ export interface LibraryHttpClient {
60
60
  post<T = unknown>(url: string, data?: unknown, config?: any): Promise<{
61
61
  data: T;
62
62
  }>;
63
+ postForm<T = unknown>(url: string, data?: unknown, config?: any): Promise<{
64
+ data: T;
65
+ }>;
63
66
  put<T = unknown>(url: string, data?: unknown, config?: any): Promise<{
64
67
  data: T;
65
68
  }>;
package/dist/library.js CHANGED
@@ -588,7 +588,7 @@ export class LibraryApi {
588
588
  }
589
589
  };
590
590
  }
591
- const res = await this.client.post(this.getUrl('/medias'), formData, config);
591
+ const res = await this.client.postForm(this.getUrl('/medias'), formData, config);
592
592
  return res.data;
593
593
  }
594
594
  }