@placeos/ts-client 4.2.3 → 4.2.5

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/package.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "4.2.3",
2
+ "version": "4.2.5",
3
3
  "license": "MIT",
4
4
  "name": "@placeos/ts-client",
5
5
  "author": "Alex Sorafumo <alex@place.tech>",
@@ -1,6 +1,6 @@
1
- import { Observable, of, throwError } from 'rxjs';
1
+ import { Observable, throwError } from 'rxjs';
2
2
  import { fromFetch } from 'rxjs/fetch';
3
- import { filter, mergeMap, retryWhen, switchMap, take } from 'rxjs/operators';
3
+ import { filter, retry, switchMap, take } from 'rxjs/operators';
4
4
 
5
5
  import {
6
6
  apiKey,
@@ -225,17 +225,26 @@ export async function transform(
225
225
  return await resp.json().catch(() => ({}));
226
226
  case 'text':
227
227
  return await resp.text();
228
+ case 'void':
229
+ return;
230
+ default:
231
+ return await resp.json().catch(() => ({}));
228
232
  }
229
233
  }
230
234
 
231
235
  /**
232
236
  * @private
233
237
  */
234
- const reloadAuth = () => {
238
+ const reloadAuth = (): Promise<void> => {
235
239
  invalidateToken();
236
- refreshAuthority().then(
237
- () => null,
238
- () => setTimeout(() => reloadAuth(), 1000),
240
+ return refreshAuthority().then(
241
+ () => Promise.resolve(),
242
+ () =>
243
+ new Promise<void>((resolve) => {
244
+ setTimeout(() => {
245
+ reloadAuth().then(() => resolve());
246
+ }, 1000);
247
+ }),
239
248
  );
240
249
  };
241
250
 
@@ -255,7 +264,7 @@ export function request(
255
264
  m: HttpVerb,
256
265
  url: string,
257
266
  body?: any,
258
- ) => Observable<HttpResponse> | null = mockRequest,
267
+ ) => Observable<HashMap | string | void> | null = mockRequest,
259
268
  success: (
260
269
  e: Response,
261
270
  t: HttpResponseType,
@@ -263,12 +272,12 @@ export function request(
263
272
  ): Observable<HttpResponse> {
264
273
  if (is_mock()) {
265
274
  const request_obs = mock_handler(method, url, options?.body);
266
- if (request_obs) {
267
- return request_obs;
268
- }
275
+ if (request_obs) return request_obs;
269
276
  }
270
277
  options.headers = options.headers || {};
271
- options.headers['Content-Type'] = `application/json`;
278
+ if (!options.headers['Content-Type'] && !options.headers['content-type']) {
279
+ options.headers['Content-Type'] = `application/json`;
280
+ }
272
281
  return listenForToken().pipe(
273
282
  filter((_) => _),
274
283
  take(1),
@@ -278,34 +287,57 @@ export function request(
278
287
  } else {
279
288
  options.headers!.Authorization = `Bearer ${token()}`;
280
289
  }
281
- return fromFetch(url, {
290
+ const fetchOptions: any = {
282
291
  ...options,
283
- body: JSON.stringify(options.body),
284
292
  method,
285
293
  credentials: 'same-origin',
286
- });
294
+ };
295
+
296
+ // Only add body for methods that support it and when body exists
297
+ if (
298
+ ['POST', 'PUT', 'PATCH'].includes(method) &&
299
+ options.body !== undefined
300
+ ) {
301
+ fetchOptions.body =
302
+ typeof options.body === 'string'
303
+ ? options.body
304
+ : JSON.stringify(options.body);
305
+ }
306
+
307
+ return fromFetch(url, fetchOptions) as Observable<Response>;
287
308
  }),
288
- switchMap((resp) => {
309
+ switchMap((resp: Response) => {
289
310
  if (resp.ok) {
290
311
  return success(resp, options.response_type as any);
291
312
  }
292
313
  return throwError(resp);
293
314
  }),
294
- retryWhen((attempts: Observable<any>) =>
295
- attempts.pipe(
296
- mergeMap((error, i) => {
315
+ retry({
316
+ count: 4,
317
+ delay: (error, retry_count) => {
318
+ return new Observable<number>((subscriber) => {
297
319
  if (error.status === 511) {
298
320
  sendToLogin(authority()!);
299
- return of(error);
321
+ subscriber.error(error);
322
+ return;
300
323
  }
301
- if (i + 1 > 4 || error.status !== 401) {
302
- return throwError(error || {});
324
+ if (error.status !== 401) {
325
+ subscriber.error(error || {});
326
+ return;
303
327
  }
304
328
  log('HTTP', 'Auth error', error);
305
- reloadAuth();
306
- return of(error);
307
- }),
308
- ),
309
- ),
329
+ // Wait for auth refresh before retrying with exponential backoff
330
+ const delay_ms = Math.pow(2, retry_count - 1) * 1000; // Exponential backoff: 1s, 2s, 4s, 8s
331
+ reloadAuth()
332
+ .then(() => {
333
+ subscriber.next(delay_ms);
334
+ subscriber.complete();
335
+ })
336
+ .catch(() => {
337
+ subscriber.error(error);
338
+ });
339
+ });
340
+ },
341
+ }),
310
342
  );
311
343
  }
package/src/http/mock.ts CHANGED
@@ -86,12 +86,15 @@ export function clearMockEndpoints(
86
86
  * @param handler_map Handler map to query for the request handler.
87
87
  * Defaults to the global handler map
88
88
  */
89
- export function mockRequest<T>(
89
+ export function mockRequest(
90
90
  method: HttpVerb,
91
91
  url: string,
92
92
  body?: any,
93
93
  handler_map: HashMap<MockHttpRequestHandler> = _handlers,
94
- ): Observable<T> | null {
94
+ ): Observable<HashMap | string | void> | null {
95
+ if (window.debug) {
96
+ console.log('Resolving request:', method, url, body);
97
+ }
95
98
  const handler = findRequestHandler(method, url, handler_map);
96
99
  if (handler) {
97
100
  const request = processRequest(url, handler, body);
@@ -113,7 +116,7 @@ export function findRequestHandler(
113
116
  handler_map: HashMap<MockHttpRequestHandler> = _handlers,
114
117
  ): MockHttpRequestHandler | null {
115
118
  const path = url
116
- .replace(/(http|https)?:\/\/[a-zA-Z0-9.]*:?([0-9]*)?/g, '')
119
+ .replace(/(http|https):\/\/[a-zA-Z0-9.]*:?([0-9]*)?/g, '')
117
120
  .replace(/^\//, '')
118
121
  .split('?')[0];
119
122
  const route_parts = path.split('/');
@@ -161,16 +164,16 @@ export function processRequest<T = any>(
161
164
  const parts = url
162
165
  .replace(/(http|https):\/\/[a-zA-Z0-9.]*:?([0-9]*)?/g, '')
163
166
  .split('?');
164
- const path = parts[0].replace(/^\//g, '');
167
+ const path = parts[0].replace(/^\//, '');
165
168
  const query = parts[1] || '';
166
169
  const query_params = convertPairStringToMap(query);
167
170
  // Grab route parameters from URL
168
171
  const route_parts = path.split('/');
169
172
  const route_params: HashMap = {};
170
- for (const part of handler.path_structure) {
171
- if (part) {
172
- route_params[part] =
173
- route_parts[handler.path_structure.indexOf(part)];
173
+ for (let i = 0; i < handler.path_structure.length; i++) {
174
+ const paramName = handler.path_structure[i];
175
+ if (paramName) {
176
+ route_params[paramName] = route_parts[i];
174
177
  }
175
178
  }
176
179
  const request = {
@@ -196,9 +199,15 @@ export function onMockRequest(
196
199
  handler: MockHttpRequestHandler,
197
200
  request: MockHttpRequest,
198
201
  ) {
199
- const result = handler.callback
200
- ? handler.callback(request)
201
- : handler.metadata;
202
+ let result;
203
+ try {
204
+ result = handler.callback
205
+ ? handler.callback(request)
206
+ : handler.metadata;
207
+ } catch (error) {
208
+ log('HTTP(M)', `ERROR ${request.method}:`, [request.url, error]);
209
+ throw error;
210
+ }
202
211
  const variance = handler.delay_variance || 100;
203
212
  const delay_value = handler.delay || 300;
204
213
  const delay_time =
@@ -206,3 +215,11 @@ export function onMockRequest(
206
215
  log('HTTP(M)', `RESP ${request.method}:`, [request.url, result]);
207
216
  return from([result]).pipe(delay(Math.max(200, delay_time)));
208
217
  }
218
+
219
+ /**
220
+ * Get a list of the method + endpoints that have been mocked
221
+ * @returns List of the method + endpoint that have been mocked
222
+ */
223
+ export function listMockedEndpoints(): string[] {
224
+ return Object.keys(_handlers);
225
+ }