@xh/hoist 67.0.0-SNAPSHOT.1723668375053 → 67.0.0-SNAPSHOT.1723839594443

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/CHANGELOG.md CHANGED
@@ -8,14 +8,19 @@
8
8
  * New `FetchService` members: `autoGenCorrelationIds`, `genCorrelationId` and
9
9
  `correlationIdHeaderKey` to support generation and inclusion of Correlation IDs on outbound
10
10
  request headers.
11
- * Correlation ID assignment is available via:
11
+ * Correlation IDs are assigned via:
12
12
  * `FetchOptions.correlationId` - specify an ID to be used on a particular request or `true`
13
13
  to use a UUID generated by Hoist (see `FetchService.genCorrelationId()`).
14
- * `TrackOptions.correlationId` - specify an ID for a tracked activity, if not already
15
- relayed from a `FetchService` response.
16
- * If set on a fetch request, Correlation IDs are passed through to downstream Hoist activity
17
- tracking and error reporting and are available for review in the Admin Console.
14
+ * `TrackOptions.correlationId` - specify an ID for a tracked activity, if not using the
15
+ new `FetchOptions.track` API (see below).
16
+ * If set on a fetch request, Correlation IDs are passed through to downstream error reporting
17
+ and are available for review in the Admin Console.
18
18
  * New `XH.genUUID()` method to generate UUIDs for use as Correlation IDs or elsewhere.
19
+ * New `FetchOptions.track` - specify `TrackOptions` or message `string` to track a request via
20
+ Hoist activity tracking. The request's Correlation ID and LoadSpec will be included automatically.
21
+ * New global interceptors on `FetchService`. See `FetchService.addInterceptor()`.
22
+ * New property `FetchOptions.asJson` to instruct `FetchService` to decode an HTTP response as JSON.
23
+ Note that `FetchService` methods suffixed with `Json` will set this property automatically.
19
24
  * `GridModel` will now accept `contextMenu: false` to omit context menus.
20
25
 
21
26
  ### 🐞 Bug Fixes
@@ -33,7 +38,7 @@
33
38
 
34
39
  ### 📚 Libraries
35
40
 
36
- * uuid `added @ 10.0`
41
+ * short-unique-id `added @ 5.2`
37
42
 
38
43
  ## 66.1.1 - 2024-08-01
39
44
 
@@ -9,7 +9,7 @@ import { AppContainerModel } from '../appcontainer/AppContainerModel';
9
9
  import { BannerModel } from '../appcontainer/BannerModel';
10
10
  import { ToastModel } from '../appcontainer/ToastModel';
11
11
  import '../styles/XH.scss';
12
- import { AppSpec, AppState, AppSuspendData, BannerSpec, ExceptionHandler, ExceptionHandlerOptions, FetchResponse, HoistAppModel, HoistException, HoistService, HoistServiceClass, HoistUser, MessageSpec, PageState, PlainObject, SizingMode, TaskObserver, Theme, ToastSpec, TrackOptions } from './';
12
+ import { AppSpec, AppState, AppSuspendData, BannerSpec, ExceptionHandler, ExceptionHandlerOptions, HoistAppModel, HoistException, HoistService, HoistServiceClass, HoistUser, MessageSpec, PageState, PlainObject, SizingMode, TaskObserver, Theme, ToastSpec, TrackOptions } from './';
13
13
  import { HoistModel, ModelSelector, RefreshContextModel } from './model';
14
14
  export declare const MIN_HOIST_CORE_VERSION = "18.0";
15
15
  /**
@@ -117,7 +117,7 @@ export declare class XHApi {
117
117
  * Send a request via the underlying fetch API.
118
118
  * @see FetchService.fetch
119
119
  */
120
- fetch(opts: FetchOptions): Promise<FetchResponse>;
120
+ fetch(opts: FetchOptions): Promise<any>;
121
121
  /**
122
122
  * Send an HTTP request and decode the response as JSON.
123
123
  * @see FetchService.fetchJson
@@ -403,6 +403,7 @@ export declare class XHApi {
403
403
  */
404
404
  genUUID(): string;
405
405
  private get acm();
406
+ private shortUniqueId;
406
407
  }
407
408
  /** The app-wide singleton instance. */
408
409
  export declare const XH: XHApi;
@@ -1,5 +1,4 @@
1
1
  import { FetchOptions } from '@xh/hoist/svc';
2
- import { FetchResponse } from '../';
3
2
  import { FetchException, HoistException, TimeoutException, TimeoutExceptionConfig } from './Types';
4
3
  /**
5
4
  * Standardized Exception/Error objects.
@@ -26,9 +25,10 @@ export declare class Exception {
26
25
  /**
27
26
  * Create an Error to throw when a fetch call returns a !ok response.
28
27
  * @param fetchOptions - original options passed to FetchService.
29
- * @param fetchResponse - return value of native fetch, as enhanced by FetchService.
28
+ * @param response - return value of native fetch.
29
+ * @param responseText - optional additional details from the server.
30
30
  */
31
- static fetchError(fetchOptions: FetchOptions, fetchResponse: FetchResponse): FetchException;
31
+ static fetchError(fetchOptions: FetchOptions, response: Response, responseText?: string): FetchException;
32
32
  /**
33
33
  * Create an Error to throw when a fetchJson call encounters a SyntaxError.
34
34
  * @param fetchOptions - original options passed to FetchService.
@@ -19,15 +19,6 @@ export interface HoistUser {
19
19
  hasRole(s: string): boolean;
20
20
  hasGate(s: string): boolean;
21
21
  }
22
- /**
23
- * Enhanced Response returned by FetchService.
24
- */
25
- export interface FetchResponse extends Response {
26
- /**
27
- * Property containing the already-awaited output of `response.text()`.
28
- */
29
- responseText: string;
30
- }
31
22
  /**
32
23
  * Options for showing a "toast" notification that appears and then automatically dismisses.
33
24
  */
@@ -170,10 +161,7 @@ export interface TrackOptions {
170
161
  message: string;
171
162
  /** App-supplied category.*/
172
163
  category?: string;
173
- /**
174
- * Correlation ID to save along with track log. If not provided, will attempt to source from the
175
- * underlying Fetch Request.
176
- */
164
+ /** Correlation ID to save along with track log. */
177
165
  correlationId?: string;
178
166
  /** App-supplied data to save along with track log.*/
179
167
  data?: PlainObject | PlainObject[];
@@ -58,11 +58,6 @@ declare global {
58
58
  * @param options - TrackOptions, or simply a message string.
59
59
  */
60
60
  track(options: TrackOptions | string): Promise<T>;
61
- /**
62
- * Set by FetchService to relay correlation IDs to downstream error handling and tracking.
63
- * @internal
64
- */
65
- correlationId?: string;
66
61
  }
67
62
  }
68
63
  /**
@@ -1,4 +1,4 @@
1
- import { Awaitable, FetchResponse, HoistService, LoadSpec, PlainObject } from '@xh/hoist/core';
1
+ import { Awaitable, HoistService, LoadSpec, PlainObject, TrackOptions } from '@xh/hoist/core';
2
2
  import { PromiseTimeoutSpec } from '@xh/hoist/promise';
3
3
  import { StatusCodes } from 'http-status-codes';
4
4
  import { IStringifyOptions } from 'qs';
@@ -25,6 +25,8 @@ export declare class FetchService extends HoistService {
25
25
  static instance: FetchService;
26
26
  NO_JSON_RESPONSES: StatusCodes[];
27
27
  private autoAborters;
28
+ private _defaultHeaders;
29
+ private _interceptors;
28
30
  /** True to auto-generate a Correlation ID for each request unless otherwise specified. */
29
31
  autoGenCorrelationIds: boolean;
30
32
  /**
@@ -34,35 +36,45 @@ export declare class FetchService extends HoistService {
34
36
  genCorrelationId: () => string;
35
37
  /** Request header name to be used for Correlation ID tracking. */
36
38
  correlationIdHeaderKey: string;
37
- /**
38
- * Timeout to be used for all requests made via this service that do not themselves spec a
39
- * custom timeout.
40
- */
39
+ /** Default timeout to be used for all requests made via this service */
41
40
  defaultTimeout: PromiseTimeoutSpec;
42
- private _defaultHeaders;
43
- get defaultHeaders(): Array<PlainObject | ((arg: FetchOptions) => Awaitable<PlainObject>)>;
44
- /**
45
- * Set default headers to be sent with all subsequent requests.
46
- * @param headers - to be sent with all fetch requests, or a function to generate.
47
- * @deprecated use addDefaultHeaders instead.
48
- */
49
- setDefaultHeaders(headers: PlainObject | ((arg: FetchOptions) => Awaitable<PlainObject>)): void;
41
+ /** Default headers to be sent with all subsequent requests. */
42
+ get defaultHeaders(): DefaultHeaders[];
43
+ /**
44
+ * Promise handlers to be executed before fufilling or rejecting returned Promise.
45
+ *
46
+ * Use the `onRejected` handler for apps requiring common handling for particular exceptions.
47
+ * Useful for recognizing 401s (i.e. session end), or wrapping, logging, or enhancing exceptions.
48
+ * The simplest onRejected handler will simply rethrow the passed exception, or a wrapped version of it.
49
+ * Such handlers may also return `never()` to prevent further processing of the request -- this
50
+ * is useful, i.e. if the handler is going to redirect the entire app, or otherwise end normal
51
+ * app processing. Rejected handlers may also be able to retry and return valid results via
52
+ * another call to fetch.
53
+ *
54
+ * Use the `onFulfilled` hander for enhancing, tracking, or even rejecting "successful" returns.
55
+ * For example, a handler of this form could be used to transform a 200 response returned by
56
+ * an API with an "error" flag into a proper client-side exception.
57
+ */
58
+ addInterceptor(handler: FetchInterceptor): void;
50
59
  /**
51
60
  * Add default headers to be sent with all subsequent requests.
52
61
  * @param headers - to be sent with all fetch requests, or a function to generate.
53
62
  */
54
- addDefaultHeaders(headers: PlainObject | ((arg: FetchOptions) => Awaitable<PlainObject>)): void;
55
- /**
56
- * Set the timeout (default 30 seconds) to be used for all requests made via this service that
57
- * do not themselves spec a custom timeout.
58
- * @deprecated modify `defaultTimeout` directly instead.
59
- */
60
- setDefaultTimeout(timeout: PromiseTimeoutSpec): void;
63
+ addDefaultHeaders(headers: DefaultHeaders): void;
61
64
  /**
62
65
  * Send a request via the underlying fetch API.
63
- * @returns Promise which resolves to a Fetch Response.
64
- */
65
- fetch(opts: FetchOptions): Promise<FetchResponse>;
66
+ *
67
+ * This is the main entry point for this API, and can be used to satisfy all
68
+ * requests. Other shortcut variants will delegate to this method, after setting
69
+ * default options and pre-processing content.
70
+ *
71
+ * Set `asJson` to true return a parsed JSON result, rather than the raw Response.
72
+ * Note that shortcut variant of this method (e.g. `fetchJson`, `postJson`) will set this
73
+ * flag for you.
74
+ *
75
+ * @returns Promise which resolves to a Response or JSON.
76
+ */
77
+ fetch(opts: FetchOptions): Promise<any>;
66
78
  /**
67
79
  * Send an HTTP request and decode the response as JSON.
68
80
  * @returns the decoded JSON object, or null if the response has status in {@link NO_JSON_RESPONSES}.
@@ -98,15 +110,35 @@ export declare class FetchService extends HoistService {
98
110
  * @returns false if no request pending for the given key.
99
111
  */
100
112
  abort(autoAbortKey: string): boolean;
101
- /** Resolve convenience options for Correlation ID to server-ready string */
113
+ /**
114
+ * Set the timeout (default 30 seconds) to be used for all requests made via this service that
115
+ * do not themselves spec a custom timeout.
116
+ * @deprecated modify `defaultTimeout` directly instead.
117
+ */
118
+ setDefaultTimeout(timeout: PromiseTimeoutSpec): void;
119
+ /**
120
+ * Set default headers to be sent with all subsequent requests.
121
+ * @param headers - to be sent with all fetch requests, or a function to generate.
122
+ * @deprecated use addDefaultHeaders instead.
123
+ */
124
+ setDefaultHeaders(headers: DefaultHeaders): void;
125
+ private fetchInternalAsync;
126
+ private sendJsonInternalAsync;
102
127
  private withCorrelationId;
103
128
  private withDefaultHeadersAsync;
104
129
  private managedFetchAsync;
105
- private fetchInternalAsync;
106
- private sendJsonInternalAsync;
130
+ private abortableFetchAsync;
131
+ private parseJsonAsync;
107
132
  private safeResponseTextAsync;
108
133
  private qsFilterFn;
109
134
  }
135
+ /** Headers to be applied to all requests. Specified as object, or dynamic function to create. */
136
+ export type DefaultHeaders = PlainObject | ((opts: FetchOptions) => Awaitable<PlainObject>);
137
+ /** Handlers to be executed before fufilling or rejecting any exception to caller. */
138
+ export interface FetchInterceptor {
139
+ onFulfilled: (opts: FetchOptions, value: any) => Promise<any>;
140
+ onRejected: (opts: FetchOptions, cause: unknown) => Promise<any>;
141
+ }
110
142
  /**
111
143
  * Standard options to pass through to fetch, with some additions.
112
144
  * See MDN for available options - {@link https://developer.mozilla.org/en-US/docs/Web/API/Request}.
@@ -163,4 +195,13 @@ export interface FetchOptions {
163
195
  * aborted in favor of the new request.
164
196
  */
165
197
  autoAbortKey?: string;
198
+ /**
199
+ * True to decode the HTTP response as JSON. Default false.
200
+ */
201
+ asJson?: boolean;
202
+ /**
203
+ * If set, the request will be tracked via Hoist activity tracking. (Do not set `correlationId`
204
+ * here - use the top-level `correlationId` property instead.)
205
+ */
206
+ track?: string | TrackOptions;
166
207
  }
package/core/XH.ts CHANGED
@@ -46,7 +46,6 @@ import {
46
46
  Exception,
47
47
  ExceptionHandler,
48
48
  ExceptionHandlerOptions,
49
- FetchResponse,
50
49
  HoistAppModel,
51
50
  HoistException,
52
51
  HoistService,
@@ -64,7 +63,7 @@ import {
64
63
  import {installServicesAsync} from './impl/InstallServices';
65
64
  import {instanceManager} from './impl/InstanceManager';
66
65
  import {HoistModel, ModelSelector, RefreshContextModel} from './model';
67
- import {v4} from 'uuid';
66
+ import ShortUniqueId from 'short-unique-id';
68
67
 
69
68
  export const MIN_HOIST_CORE_VERSION = '18.0';
70
69
 
@@ -263,7 +262,7 @@ export class XHApi {
263
262
  * Send a request via the underlying fetch API.
264
263
  * @see FetchService.fetch
265
264
  */
266
- fetch(opts: FetchOptions): Promise<FetchResponse> {
265
+ fetch(opts: FetchOptions): Promise<any> {
267
266
  return this.fetchService.fetch(opts);
268
267
  }
269
268
 
@@ -785,7 +784,7 @@ export class XHApi {
785
784
  * Generate a universally unique identifier (UUID). Useful for generating Correlation IDs.
786
785
  */
787
786
  genUUID(): string {
788
- return v4();
787
+ return this.shortUniqueId.rnd();
789
788
  }
790
789
 
791
790
  //----------------
@@ -794,6 +793,8 @@ export class XHApi {
794
793
  private get acm(): AppContainerModel {
795
794
  return this.appContainerModel;
796
795
  }
796
+
797
+ private shortUniqueId = new ShortUniqueId({length: 16});
797
798
  }
798
799
 
799
800
  /** The app-wide singleton instance. */
@@ -5,7 +5,7 @@
5
5
  * Copyright © 2024 Extremely Heavy Industries Inc.
6
6
  */
7
7
  import {FetchOptions} from '@xh/hoist/svc';
8
- import {FetchResponse, PlainObject, XH} from '../';
8
+ import {PlainObject, XH} from '../';
9
9
  import {isPlainObject} from 'lodash';
10
10
 
11
11
  import {FetchException, HoistException, TimeoutException, TimeoutExceptionConfig} from './Types';
@@ -58,10 +58,15 @@ export class Exception {
58
58
  /**
59
59
  * Create an Error to throw when a fetch call returns a !ok response.
60
60
  * @param fetchOptions - original options passed to FetchService.
61
- * @param fetchResponse - return value of native fetch, as enhanced by FetchService.
61
+ * @param response - return value of native fetch.
62
+ * @param responseText - optional additional details from the server.
62
63
  */
63
- static fetchError(fetchOptions: FetchOptions, fetchResponse: FetchResponse): FetchException {
64
- const {headers, status, statusText, responseText} = fetchResponse,
64
+ static fetchError(
65
+ fetchOptions: FetchOptions,
66
+ response: Response,
67
+ responseText: string = null
68
+ ): FetchException {
69
+ const {headers, status, statusText} = response,
65
70
  defaults = {
66
71
  name: 'HTTP Error ' + (status || ''),
67
72
  message: statusText,
@@ -28,16 +28,6 @@ export interface HoistUser {
28
28
  hasGate(s: string): boolean;
29
29
  }
30
30
 
31
- /**
32
- * Enhanced Response returned by FetchService.
33
- */
34
- export interface FetchResponse extends Response {
35
- /**
36
- * Property containing the already-awaited output of `response.text()`.
37
- */
38
- responseText: string;
39
- }
40
-
41
31
  /**
42
32
  * Options for showing a "toast" notification that appears and then automatically dismisses.
43
33
  */
@@ -213,10 +203,7 @@ export interface TrackOptions {
213
203
  /** App-supplied category.*/
214
204
  category?: string;
215
205
 
216
- /**
217
- * Correlation ID to save along with track log. If not provided, will attempt to source from the
218
- * underlying Fetch Request.
219
- */
206
+ /** Correlation ID to save along with track log. */
220
207
  correlationId?: string;
221
208
 
222
209
  /** App-supplied data to save along with track log.*/
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xh/hoist",
3
- "version": "67.0.0-SNAPSHOT.1723668375053",
3
+ "version": "67.0.0-SNAPSHOT.1723839594443",
4
4
  "description": "Hoist add-on for building and deploying React Applications.",
5
5
  "repository": "github:xh/hoist-react",
6
6
  "homepage": "https://xh.io",
@@ -83,9 +83,9 @@
83
83
  "router5": "~7.0.2",
84
84
  "router5-plugin-browser": "~7.0.2",
85
85
  "semver": "~7.6.0",
86
+ "short-unique-id": "~5.2.0",
86
87
  "store2": "~2.13.1",
87
- "ua-parser-js": "~1.0.2",
88
- "uuid": "~10.0.0"
88
+ "ua-parser-js": "~1.0.2"
89
89
  },
90
90
  "peerDependencies": {
91
91
  "react": "~18.2.0",
@@ -92,12 +92,6 @@ declare global {
92
92
  * @param options - TrackOptions, or simply a message string.
93
93
  */
94
94
  track(options: TrackOptions | string): Promise<T>;
95
-
96
- /**
97
- * Set by FetchService to relay correlation IDs to downstream error handling and tracking.
98
- * @internal
99
- */
100
- correlationId?: string;
101
95
  }
102
96
  }
103
97
 
@@ -206,7 +200,7 @@ const enhancePromise = promisePrototype => {
206
200
  const startTime = Date.now();
207
201
  return this.finally(() => {
208
202
  options.elapsed = Date.now() - startTime;
209
- XH.track({correlationId: this.correlationId, ...options});
203
+ XH.track(options);
210
204
  });
211
205
  },
212
206
 
@@ -11,7 +11,6 @@ import {Timer} from '@xh/hoist/utils/async';
11
11
  import {MINUTES, olderThan, ONE_MINUTE, SECONDS} from '@xh/hoist/utils/datetime';
12
12
  import {isJSON, throwIf} from '@xh/hoist/utils/js';
13
13
  import {find, forEach, isEmpty, isObject, keys, pickBy, union} from 'lodash';
14
- import {v4 as uuid} from 'uuid';
15
14
 
16
15
  export type LoginMethod = 'REDIRECT' | 'POPUP';
17
16
 
@@ -255,7 +254,7 @@ export abstract class BaseOAuthClient<C extends BaseOAuthClientConfig<S>, S> ext
255
254
  protected captureRedirectState(): string {
256
255
  const {pathname, search} = window.location,
257
256
  state = {
258
- key: uuid(),
257
+ key: XH.genUUID(),
259
258
  timestamp: Date.now(),
260
259
  pathname,
261
260
  search