@xh/hoist 76.0.0-SNAPSHOT.1758215282397 → 76.0.0-SNAPSHOT.1758297294348
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 +1 -3
- package/admin/tabs/userData/roles/graph/RoleGraph.ts +1 -2
- package/appcontainer/AppContainerModel.ts +1 -2
- package/appcontainer/ExceptionDialogModel.ts +2 -1
- package/build/types/appcontainer/ExceptionDialogModel.d.ts +2 -1
- package/build/types/cmp/grid/Grid.d.ts +3 -3
- package/build/types/core/{exception/ExceptionHandler.d.ts → ExceptionHandler.d.ts} +2 -1
- package/build/types/core/XH.d.ts +3 -2
- package/build/types/core/index.d.ts +1 -3
- package/build/types/core/types/Types.d.ts +0 -10
- package/build/types/desktop/cmp/appOption/AutoRefreshAppOption.d.ts +5 -5
- package/build/types/desktop/cmp/appOption/ThemeAppOption.d.ts +5 -5
- package/build/types/desktop/cmp/rest/impl/RestFormModel.d.ts +1 -1
- package/build/types/exception/Exception.d.ts +24 -0
- package/build/types/{core/exception → exception}/Types.d.ts +1 -18
- package/build/types/exception/index.d.ts +2 -0
- package/build/types/kit/swiper/index.d.ts +1 -1
- package/build/types/svc/FetchService.d.ts +55 -0
- package/build/types/utils/js/LangUtils.d.ts +1 -27
- package/build/types/utils/{log → js}/LogUtils.d.ts +37 -1
- package/build/types/utils/js/index.d.ts +2 -0
- package/cmp/ag-grid/AgGrid.ts +1 -1
- package/cmp/chart/Chart.ts +1 -2
- package/cmp/chart/impl/ChartContextMenuItems.ts +1 -1
- package/cmp/grid/columns/Column.ts +1 -2
- package/cmp/grid/helpers/GridCountLabel.ts +1 -2
- package/cmp/grid/impl/ColumnWidthCalculator.ts +1 -2
- package/cmp/grid/impl/Utils.ts +1 -1
- package/cmp/relativetimestamp/RelativeTimestamp.ts +1 -2
- package/cmp/treemap/TreeMap.ts +1 -2
- package/core/{exception/ExceptionHandler.ts → ExceptionHandler.ts} +3 -4
- package/core/HoistBase.ts +3 -2
- package/core/HoistBaseDecorators.ts +1 -2
- package/core/HoistComponent.ts +1 -2
- package/core/XH.ts +3 -4
- package/core/index.ts +1 -3
- package/core/load/LoadSupport.ts +1 -2
- package/core/persist/PersistenceProvider.ts +1 -2
- package/core/types/Types.ts +0 -6
- package/data/filter/Utils.ts +1 -1
- package/data/impl/RecordSet.ts +1 -2
- package/desktop/cmp/button/grid/ColAutosizeButton.ts +1 -2
- package/desktop/cmp/button/grid/ColChooserButton.ts +1 -2
- package/desktop/cmp/button/grid/ExpandToLevelButton.ts +1 -2
- package/desktop/cmp/button/grid/ExportButton.ts +1 -2
- package/desktop/cmp/button/panel/ModalToggleButton.ts +1 -2
- package/desktop/cmp/button/zoneGrid/ZoneMapperButton.ts +1 -2
- package/desktop/cmp/dash/container/impl/DashContainerUtils.ts +1 -2
- package/desktop/cmp/form/FormField.ts +1 -2
- package/desktop/cmp/grid/editors/BooleanEditor.ts +1 -1
- package/desktop/cmp/panel/Panel.ts +1 -1
- package/desktop/hooks/UseContextMenu.ts +1 -1
- package/exception/Exception.ts +81 -0
- package/{core/exception → exception}/Types.ts +1 -22
- package/{utils/log → exception}/index.ts +2 -2
- package/kit/ag-grid/index.ts +1 -2
- package/kit/highcharts/index.ts +1 -2
- package/mobile/cmp/button/grid/ColAutosizeButton.ts +1 -2
- package/mobile/cmp/button/grid/ColChooserButton.ts +1 -2
- package/mobile/cmp/button/grid/ExpandCollapseButton.ts +1 -2
- package/mobile/cmp/button/grid/ExpandToLevelButton.ts +1 -2
- package/mobile/cmp/button/zoneGrid/ZoneMapperButton.ts +1 -2
- package/mobile/cmp/panel/Panel.ts +1 -1
- package/mobx/overrides.ts +1 -1
- package/package.json +1 -1
- package/promise/Promise.ts +3 -3
- package/security/BaseOAuthClient.ts +3 -3
- package/security/msal/MsalClient.ts +1 -2
- package/svc/ChangelogService.ts +1 -1
- package/svc/EnvironmentService.ts +1 -2
- package/svc/FetchService.ts +208 -16
- package/tsconfig.tsbuildinfo +1 -1
- package/utils/async/Timer.ts +1 -2
- package/utils/js/Decorators.ts +4 -4
- package/utils/js/LangUtils.ts +2 -66
- package/utils/{log → js}/LogUtils.ts +77 -19
- package/utils/js/index.ts +2 -0
- package/build/types/core/exception/Exception.d.ts +0 -61
- package/build/types/utils/log/index.d.ts +0 -1
- package/build/types/utils/version/index.d.ts +0 -1
- package/core/exception/Exception.ts +0 -256
- package/utils/version/index.ts +0 -8
- /package/build/types/utils/{version → js}/VersionUtils.d.ts +0 -0
- /package/utils/{version → js}/VersionUtils.ts +0 -0
package/utils/async/Timer.ts
CHANGED
|
@@ -7,8 +7,7 @@
|
|
|
7
7
|
import {XH} from '@xh/hoist/core';
|
|
8
8
|
import {wait} from '@xh/hoist/promise';
|
|
9
9
|
import {MILLISECONDS, MINUTES, olderThan} from '@xh/hoist/utils/datetime';
|
|
10
|
-
import {throwIf} from '@xh/hoist/utils/js';
|
|
11
|
-
import {logError, logWarn} from '@xh/hoist/utils/log';
|
|
10
|
+
import {logError, logWarn, throwIf} from '@xh/hoist/utils/js';
|
|
12
11
|
import {isBoolean, isFinite, isFunction, isNil, isString, pull} from 'lodash';
|
|
13
12
|
|
|
14
13
|
/**
|
package/utils/js/Decorators.ts
CHANGED
|
@@ -4,10 +4,10 @@
|
|
|
4
4
|
*
|
|
5
5
|
* Copyright © 2025 Extremely Heavy Industries Inc.
|
|
6
6
|
*/
|
|
7
|
-
import {
|
|
7
|
+
import {Exception} from '@xh/hoist/exception';
|
|
8
8
|
import {debounce, isFunction} from 'lodash';
|
|
9
|
-
import {getOrCreate, throwIf
|
|
10
|
-
import {withDebug, withInfo} from '
|
|
9
|
+
import {getOrCreate, throwIf} from './LangUtils';
|
|
10
|
+
import {withDebug, warnIf, withInfo} from './LogUtils';
|
|
11
11
|
|
|
12
12
|
/**
|
|
13
13
|
* Decorates a class method so that it is debounced by the specified duration.
|
|
@@ -106,7 +106,7 @@ export function abstract(target, key, descriptor) {
|
|
|
106
106
|
return {
|
|
107
107
|
...descriptor,
|
|
108
108
|
[baseFnName]: function () {
|
|
109
|
-
throw
|
|
109
|
+
throw Exception.create(`${key} must be implemented by ${this.constructor.name}`);
|
|
110
110
|
}
|
|
111
111
|
};
|
|
112
112
|
}
|
package/utils/js/LangUtils.ts
CHANGED
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
*
|
|
5
5
|
* Copyright © 2025 Extremely Heavy Industries Inc.
|
|
6
6
|
*/
|
|
7
|
-
import type {PlainObject, Thunkable
|
|
8
|
-
import {Exception} from '@xh/hoist/
|
|
7
|
+
import type {PlainObject, Thunkable} from '@xh/hoist/core';
|
|
8
|
+
import {Exception} from '@xh/hoist/exception';
|
|
9
9
|
import {
|
|
10
10
|
flatMap,
|
|
11
11
|
forOwn,
|
|
@@ -125,24 +125,6 @@ export function throwIf(condition: any, message: unknown) {
|
|
|
125
125
|
}
|
|
126
126
|
}
|
|
127
127
|
|
|
128
|
-
/**
|
|
129
|
-
* Log a warning to the console if a condition evaluates as truthy.
|
|
130
|
-
*/
|
|
131
|
-
export function warnIf(condition: any, message: any) {
|
|
132
|
-
if (condition) {
|
|
133
|
-
console.warn(message);
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
/**
|
|
138
|
-
* Log an error to the console if a condition evaluates as truthy.
|
|
139
|
-
*/
|
|
140
|
-
export function errorIf(condition: any, message: any) {
|
|
141
|
-
if (condition) {
|
|
142
|
-
console.error(message);
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
|
|
146
128
|
/**
|
|
147
129
|
* Instantiate a singleton object of a class, and place a reference to the created
|
|
148
130
|
* object in a static property on the class.
|
|
@@ -156,52 +138,6 @@ export function createSingleton<T>(clazz: new () => T): T {
|
|
|
156
138
|
return (clazz['instance'] = new clazz());
|
|
157
139
|
}
|
|
158
140
|
|
|
159
|
-
export interface APIWarnOptions {
|
|
160
|
-
/**
|
|
161
|
-
* If provided and undefined, this method will be a no-op.
|
|
162
|
-
* Useful for testing if a parameter has been provided in caller.
|
|
163
|
-
*/
|
|
164
|
-
test?: any;
|
|
165
|
-
|
|
166
|
-
/** Version when this API will no longer be supported or this warning should be removed. */
|
|
167
|
-
v?: string;
|
|
168
|
-
|
|
169
|
-
/** An additional message. Can contain suggestions for alternatives. */
|
|
170
|
-
msg?: string;
|
|
171
|
-
|
|
172
|
-
/** Source of message for labeling log message. */
|
|
173
|
-
source?: LogSource;
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
/**
|
|
177
|
-
* Document and prevent usage of a removed parameter.
|
|
178
|
-
*/
|
|
179
|
-
export function apiRemoved(name: string, opts: APIWarnOptions = {}) {
|
|
180
|
-
if ('test' in opts && isUndefined(opts.test)) return;
|
|
181
|
-
|
|
182
|
-
const src = opts.source ? `[${opts.source}] ` : '',
|
|
183
|
-
msg = opts.msg ? ` ${opts.msg}.` : '';
|
|
184
|
-
throw Exception.create(`${src}The use of '${name}' is no longer supported.${msg}`);
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
/**
|
|
188
|
-
* Document and warn on usage of a deprecated API
|
|
189
|
-
*
|
|
190
|
-
* @param name - the name of the deprecated parameter
|
|
191
|
-
*/
|
|
192
|
-
const _seenWarnings = {};
|
|
193
|
-
export function apiDeprecated(name: string, opts: APIWarnOptions = {}) {
|
|
194
|
-
if ('test' in opts && isUndefined(opts.test)) return;
|
|
195
|
-
|
|
196
|
-
const v = opts.v ?? 'a future release',
|
|
197
|
-
msg = opts.msg ?? '',
|
|
198
|
-
warn = `The use of '${name}' has been deprecated and will be removed in ${v}. ${msg}`;
|
|
199
|
-
if (!_seenWarnings[warn]) {
|
|
200
|
-
console.warn(warn, opts.source);
|
|
201
|
-
_seenWarnings[warn] = true;
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
|
|
205
141
|
/**
|
|
206
142
|
* Throw an exception if the provided object or collection is empty, as per lodash isEmpty().
|
|
207
143
|
*
|
|
@@ -4,9 +4,11 @@
|
|
|
4
4
|
*
|
|
5
5
|
* Copyright © 2025 Extremely Heavy Industries Inc.
|
|
6
6
|
*/
|
|
7
|
-
import type {Some
|
|
8
|
-
import {
|
|
7
|
+
import type {Some} from '@xh/hoist/core';
|
|
8
|
+
import {Exception} from '@xh/hoist/exception';
|
|
9
|
+
import {castArray, isString, isUndefined} from 'lodash';
|
|
9
10
|
import store from 'store2';
|
|
11
|
+
import {intersperse} from './LangUtils';
|
|
10
12
|
|
|
11
13
|
/**
|
|
12
14
|
* Utility functions providing managed, structured logging to Hoist apps.
|
|
@@ -22,6 +24,29 @@ import store from 'store2';
|
|
|
22
24
|
* XH.logLevel from the console.
|
|
23
25
|
*/
|
|
24
26
|
|
|
27
|
+
/** Severity Level for log statement */
|
|
28
|
+
export type LogLevel = 'error' | 'warn' | 'info' | 'debug';
|
|
29
|
+
|
|
30
|
+
/** Object identifying the source of log statement. Typically, a javascript class */
|
|
31
|
+
export type LogSource = string | {displayName: string} | {constructor: {name: string}};
|
|
32
|
+
|
|
33
|
+
export interface APIWarnOptions {
|
|
34
|
+
/**
|
|
35
|
+
* If provided and undefined, this method will be a no-op.
|
|
36
|
+
* Useful for testing if a parameter has been provided in caller.
|
|
37
|
+
*/
|
|
38
|
+
test?: any;
|
|
39
|
+
|
|
40
|
+
/** Version when this API will no longer be supported or this warning should be removed. */
|
|
41
|
+
v?: string;
|
|
42
|
+
|
|
43
|
+
/** An additional message. Can contain suggestions for alternatives. */
|
|
44
|
+
msg?: string;
|
|
45
|
+
|
|
46
|
+
/** Source of message for labelling log message. */
|
|
47
|
+
source?: LogSource;
|
|
48
|
+
}
|
|
49
|
+
|
|
25
50
|
/**
|
|
26
51
|
* Current minimum severity for Hoist log utils (default 'info').
|
|
27
52
|
* Messages logged via managed Hoist log utils with lower severity will be ignored.
|
|
@@ -29,11 +54,7 @@ import store from 'store2';
|
|
|
29
54
|
* @internal - use public `XH.logLevel`.
|
|
30
55
|
*/
|
|
31
56
|
export function getLogLevel() {
|
|
32
|
-
|
|
33
|
-
return _logLevel;
|
|
34
|
-
} catch (e) {
|
|
35
|
-
return 'info';
|
|
36
|
-
}
|
|
57
|
+
return _logLevel;
|
|
37
58
|
}
|
|
38
59
|
|
|
39
60
|
/**
|
|
@@ -121,13 +142,58 @@ export function logWarn(msgs: Some<unknown>, source?: LogSource) {
|
|
|
121
142
|
return loggedDo(msgs, null, source, 'warn');
|
|
122
143
|
}
|
|
123
144
|
|
|
145
|
+
/**
|
|
146
|
+
* Log a warning to the console if a condition evaluates as truthy.
|
|
147
|
+
*/
|
|
148
|
+
export function warnIf(condition: any, message: any) {
|
|
149
|
+
if (condition) {
|
|
150
|
+
logWarn(message);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Log an error to the console if a condition evaluates as truthy.
|
|
156
|
+
*/
|
|
157
|
+
export function errorIf(condition: any, message: any) {
|
|
158
|
+
if (condition) {
|
|
159
|
+
logError(message);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Document and prevent usage of a removed parameter.
|
|
165
|
+
*/
|
|
166
|
+
export function apiRemoved(name: string, opts: APIWarnOptions = {}) {
|
|
167
|
+
if ('test' in opts && isUndefined(opts.test)) return;
|
|
168
|
+
|
|
169
|
+
const src = opts.source ? `[${opts.source}] ` : '',
|
|
170
|
+
msg = opts.msg ? ` ${opts.msg}.` : '';
|
|
171
|
+
throw Exception.create(`${src}The use of '${name}' is no longer supported.${msg}`);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Document and warn on usage of a deprecated API
|
|
176
|
+
*
|
|
177
|
+
* @param name - the name of the deprecated parameter
|
|
178
|
+
*/
|
|
179
|
+
const _seenWarnings = {};
|
|
180
|
+
export function apiDeprecated(name: string, opts: APIWarnOptions = {}) {
|
|
181
|
+
if ('test' in opts && isUndefined(opts.test)) return;
|
|
182
|
+
|
|
183
|
+
const v = opts.v ?? 'a future release',
|
|
184
|
+
msg = opts.msg ?? '',
|
|
185
|
+
warn = `The use of '${name}' has been deprecated and will be removed in ${v}. ${msg}`;
|
|
186
|
+
if (!_seenWarnings[warn]) {
|
|
187
|
+
logWarn(warn, opts.source);
|
|
188
|
+
_seenWarnings[warn] = true;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
124
192
|
//----------------------------------
|
|
125
193
|
// Implementation
|
|
126
194
|
//----------------------------------
|
|
127
195
|
function loggedDo<T>(messages: Some<unknown>, fn: () => T, source: LogSource, level: LogLevel): T {
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
if (_severity[level] < _severity[getLogLevel()]) {
|
|
196
|
+
if (_severity[level] < _severity[_logLevel]) {
|
|
131
197
|
return fn?.();
|
|
132
198
|
}
|
|
133
199
|
|
|
@@ -202,19 +268,11 @@ function parseSource(source: LogSource): string {
|
|
|
202
268
|
return null;
|
|
203
269
|
}
|
|
204
270
|
|
|
205
|
-
/**
|
|
206
|
-
* Intersperse a separator between each item in an array.
|
|
207
|
-
* Same method as in LangUtils, but duplicated here to avoid circular dependency.
|
|
208
|
-
*/
|
|
209
|
-
function intersperse<T>(arr: T[], separator: T): T[] {
|
|
210
|
-
return flatMap(arr, (it, idx) => {
|
|
211
|
-
return idx > 0 ? [separator, it] : [it];
|
|
212
|
-
});
|
|
213
|
-
}
|
|
214
271
|
//----------------------------------------------------------------
|
|
215
272
|
// Initialization + Level/Severity support.
|
|
216
273
|
// Initialize during parsing to make available immediately.
|
|
217
274
|
//----------------------------------------------------------------
|
|
218
275
|
let _logLevel: LogLevel = 'info';
|
|
276
|
+
const _severity: Record<LogLevel, number> = {error: 3, warn: 2, info: 1, debug: 0};
|
|
219
277
|
|
|
220
278
|
setLogLevel(store.session.get('xhLogLevel', 'info'));
|
package/utils/js/index.ts
CHANGED
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
import { FetchOptions } from '@xh/hoist/svc';
|
|
2
|
-
import { FetchException, HoistException, TimeoutException, TimeoutExceptionConfig } from './Types';
|
|
3
|
-
/**
|
|
4
|
-
* Standardized Exception/Error objects.
|
|
5
|
-
*
|
|
6
|
-
* The main entry point for this class is the create() method.
|
|
7
|
-
* Applications can use this method to create various generic runtime exceptions.
|
|
8
|
-
* @see ExceptionHandler.handleException
|
|
9
|
-
*/
|
|
10
|
-
export declare class Exception {
|
|
11
|
-
/**
|
|
12
|
-
* Create and return a HoistException
|
|
13
|
-
*
|
|
14
|
-
* See {@link XH.exception} - an alias for this factory off of `XH`.
|
|
15
|
-
*
|
|
16
|
-
* @param src - If a native JS Error, it will be enhanced into a HoistException and returned.
|
|
17
|
-
* If a plain object, all properties will be set on a new HoistException.
|
|
18
|
-
* Other inputs will be treated as the `message` of a new HoistException.
|
|
19
|
-
*/
|
|
20
|
-
static create(src: unknown): HoistException;
|
|
21
|
-
/** Create an Error for when an operation (e.g. a Promise) times out. */
|
|
22
|
-
static timeout(config: TimeoutExceptionConfig): TimeoutException;
|
|
23
|
-
/**
|
|
24
|
-
* Create an Error to throw when a fetch call returns a !ok response.
|
|
25
|
-
* @param fetchOptions - original options passed to FetchService.
|
|
26
|
-
* @param response - return value of native fetch.
|
|
27
|
-
* @param responseText - optional additional details from the server.
|
|
28
|
-
*/
|
|
29
|
-
static fetchError(fetchOptions: FetchOptions, response: Response, responseText?: string): FetchException;
|
|
30
|
-
/**
|
|
31
|
-
* Create an Error to throw when a fetchJson call encounters a SyntaxError.
|
|
32
|
-
* @param fetchOptions - original options passed to FetchService.
|
|
33
|
-
* @param cause - object thrown by native {@link response.json}.
|
|
34
|
-
*/
|
|
35
|
-
static fetchJsonParseError(fetchOptions: FetchOptions, cause: any): FetchException;
|
|
36
|
-
/**
|
|
37
|
-
* Create an Error to throw when a fetch call is aborted.
|
|
38
|
-
* @param fetchOptions - original options passed to FetchService.
|
|
39
|
-
* @param cause - object thrown by native fetch
|
|
40
|
-
*/
|
|
41
|
-
static fetchAborted(fetchOptions: FetchOptions, cause: any): FetchException;
|
|
42
|
-
/**
|
|
43
|
-
* Create an Error to throw when a fetch call times out.
|
|
44
|
-
* @param fetchOptions - original options the app passed when calling FetchService.
|
|
45
|
-
* @param cause - underlying timeout exception
|
|
46
|
-
* @param message - optional custom message
|
|
47
|
-
*
|
|
48
|
-
* @returns an exception that is both a TimeoutException, and a FetchException, with the
|
|
49
|
-
* underlying TimeoutException as its cause.
|
|
50
|
-
*/
|
|
51
|
-
static fetchTimeout(fetchOptions: FetchOptions, cause: TimeoutException, message: string): FetchException & TimeoutException;
|
|
52
|
-
/**
|
|
53
|
-
* Create an Error for when the server called by fetch does not respond
|
|
54
|
-
* @param fetchOptions - original options the app passed to FetchService.fetch
|
|
55
|
-
* @param cause - object thrown by native fetch
|
|
56
|
-
*/
|
|
57
|
-
static serverUnavailable(fetchOptions: FetchOptions, cause: any): FetchException;
|
|
58
|
-
private static createFetchException;
|
|
59
|
-
private static createInternal;
|
|
60
|
-
}
|
|
61
|
-
export declare function isHoistException(src: unknown): src is HoistException;
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export * from './LogUtils';
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export * from './VersionUtils';
|
|
@@ -1,256 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
* This file belongs to Hoist, an application development toolkit
|
|
3
|
-
* developed by Extremely Heavy Industries (www.xh.io | info@xh.io)
|
|
4
|
-
*
|
|
5
|
-
* Copyright © 2025 Extremely Heavy Industries Inc.
|
|
6
|
-
*/
|
|
7
|
-
import {PlainObject, XH} from '@xh/hoist/core';
|
|
8
|
-
import {FetchOptions} from '@xh/hoist/svc';
|
|
9
|
-
import {pluralize} from '@xh/hoist/utils/js';
|
|
10
|
-
import {isPlainObject, isString, truncate} from 'lodash';
|
|
11
|
-
import {FetchException, HoistException, TimeoutException, TimeoutExceptionConfig} from './Types';
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* Standardized Exception/Error objects.
|
|
15
|
-
*
|
|
16
|
-
* The main entry point for this class is the create() method.
|
|
17
|
-
* Applications can use this method to create various generic runtime exceptions.
|
|
18
|
-
* @see ExceptionHandler.handleException
|
|
19
|
-
*/
|
|
20
|
-
export class Exception {
|
|
21
|
-
/**
|
|
22
|
-
* Create and return a HoistException
|
|
23
|
-
*
|
|
24
|
-
* See {@link XH.exception} - an alias for this factory off of `XH`.
|
|
25
|
-
*
|
|
26
|
-
* @param src - If a native JS Error, it will be enhanced into a HoistException and returned.
|
|
27
|
-
* If a plain object, all properties will be set on a new HoistException.
|
|
28
|
-
* Other inputs will be treated as the `message` of a new HoistException.
|
|
29
|
-
*/
|
|
30
|
-
static create(src: unknown): HoistException {
|
|
31
|
-
if (isHoistException(src)) return src;
|
|
32
|
-
if (src instanceof Error) return this.createInternal({}, src);
|
|
33
|
-
|
|
34
|
-
const attributes: PlainObject = isPlainObject(src) ? src : {message: src?.toString()};
|
|
35
|
-
return this.createInternal({
|
|
36
|
-
name: 'Exception',
|
|
37
|
-
message: 'An unknown error occurred',
|
|
38
|
-
...attributes
|
|
39
|
-
});
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
/** Create an Error for when an operation (e.g. a Promise) times out. */
|
|
43
|
-
static timeout(config: TimeoutExceptionConfig): TimeoutException {
|
|
44
|
-
const {interval, ...rest} = config,
|
|
45
|
-
// Display timeout in seconds if an even multiple (or very close to it).
|
|
46
|
-
displayInterval =
|
|
47
|
-
interval % 1000 < 5
|
|
48
|
-
? pluralize('second', Math.round(interval / 1000), true)
|
|
49
|
-
: `${interval}ms`;
|
|
50
|
-
|
|
51
|
-
return this.createInternal({
|
|
52
|
-
name: 'Timeout Exception',
|
|
53
|
-
// Note FetchService.managedFetchAsync appends to this message - review if changing.
|
|
54
|
-
message: `Timed out after ${displayInterval}`,
|
|
55
|
-
isTimeout: true,
|
|
56
|
-
stack: null,
|
|
57
|
-
interval,
|
|
58
|
-
...rest
|
|
59
|
-
}) as TimeoutException;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
/**
|
|
63
|
-
* Create an Error to throw when a fetch call returns a !ok response.
|
|
64
|
-
* @param fetchOptions - original options passed to FetchService.
|
|
65
|
-
* @param response - return value of native fetch.
|
|
66
|
-
* @param responseText - optional additional details from the server.
|
|
67
|
-
*/
|
|
68
|
-
static fetchError(
|
|
69
|
-
fetchOptions: FetchOptions,
|
|
70
|
-
response: Response,
|
|
71
|
-
responseText: string = null
|
|
72
|
-
): FetchException {
|
|
73
|
-
const {headers, status, statusText} = response,
|
|
74
|
-
defaults = {
|
|
75
|
-
name: 'HTTP Error ' + (status || ''),
|
|
76
|
-
message: statusText,
|
|
77
|
-
httpStatus: status,
|
|
78
|
-
serverDetails: responseText,
|
|
79
|
-
fetchOptions
|
|
80
|
-
};
|
|
81
|
-
|
|
82
|
-
if (status === 401) {
|
|
83
|
-
return this.createFetchException({
|
|
84
|
-
...defaults,
|
|
85
|
-
name: 'Unauthorized',
|
|
86
|
-
message: 'Your session may have timed out and you may need to log in again.'
|
|
87
|
-
});
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
// Try to "smart" decode as server provided JSON Exception (with a name)
|
|
91
|
-
try {
|
|
92
|
-
const cType = headers.get('Content-Type');
|
|
93
|
-
if (cType?.includes('application/json')) {
|
|
94
|
-
const parsedResp = safeParseJson(responseText);
|
|
95
|
-
return this.createFetchException({
|
|
96
|
-
...defaults,
|
|
97
|
-
name: parsedResp?.name ?? defaults.name,
|
|
98
|
-
message: extractMessage(parsedResp, responseText, statusText),
|
|
99
|
-
isRoutine: parsedResp?.isRoutine ?? false,
|
|
100
|
-
serverDetails: parsedResp ?? responseText
|
|
101
|
-
});
|
|
102
|
-
}
|
|
103
|
-
} catch (ignored) {}
|
|
104
|
-
|
|
105
|
-
// Fall back to raw defaults
|
|
106
|
-
return this.createFetchException(defaults);
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
/**
|
|
110
|
-
* Create an Error to throw when a fetchJson call encounters a SyntaxError.
|
|
111
|
-
* @param fetchOptions - original options passed to FetchService.
|
|
112
|
-
* @param cause - object thrown by native {@link response.json}.
|
|
113
|
-
*/
|
|
114
|
-
static fetchJsonParseError(fetchOptions: FetchOptions, cause: any): FetchException {
|
|
115
|
-
return this.createFetchException({
|
|
116
|
-
name: 'JSON Parsing Error',
|
|
117
|
-
message:
|
|
118
|
-
'Error parsing the response body as JSON. The server may have returned an invalid ' +
|
|
119
|
-
'or empty response. Use "XH.fetch()" to process the response manually.',
|
|
120
|
-
fetchOptions,
|
|
121
|
-
cause
|
|
122
|
-
});
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
/**
|
|
126
|
-
* Create an Error to throw when a fetch call is aborted.
|
|
127
|
-
* @param fetchOptions - original options passed to FetchService.
|
|
128
|
-
* @param cause - object thrown by native fetch
|
|
129
|
-
*/
|
|
130
|
-
static fetchAborted(fetchOptions: FetchOptions, cause: any): FetchException {
|
|
131
|
-
return this.createFetchException({
|
|
132
|
-
name: 'Fetch Aborted',
|
|
133
|
-
message: `Fetch request aborted, url: "${fetchOptions.url}"`,
|
|
134
|
-
isRoutine: true,
|
|
135
|
-
isFetchAborted: true,
|
|
136
|
-
fetchOptions,
|
|
137
|
-
cause
|
|
138
|
-
});
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
/**
|
|
142
|
-
* Create an Error to throw when a fetch call times out.
|
|
143
|
-
* @param fetchOptions - original options the app passed when calling FetchService.
|
|
144
|
-
* @param cause - underlying timeout exception
|
|
145
|
-
* @param message - optional custom message
|
|
146
|
-
*
|
|
147
|
-
* @returns an exception that is both a TimeoutException, and a FetchException, with the
|
|
148
|
-
* underlying TimeoutException as its cause.
|
|
149
|
-
*/
|
|
150
|
-
static fetchTimeout(
|
|
151
|
-
fetchOptions: FetchOptions,
|
|
152
|
-
cause: TimeoutException,
|
|
153
|
-
message: string
|
|
154
|
-
): FetchException & TimeoutException {
|
|
155
|
-
return this.createFetchException({
|
|
156
|
-
name: 'Fetch Timeout',
|
|
157
|
-
message,
|
|
158
|
-
isFetchTimeout: true,
|
|
159
|
-
isTimeout: true,
|
|
160
|
-
interval: cause.interval,
|
|
161
|
-
fetchOptions,
|
|
162
|
-
cause
|
|
163
|
-
}) as FetchException & TimeoutException;
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
/**
|
|
167
|
-
* Create an Error for when the server called by fetch does not respond
|
|
168
|
-
* @param fetchOptions - original options the app passed to FetchService.fetch
|
|
169
|
-
* @param cause - object thrown by native fetch
|
|
170
|
-
*/
|
|
171
|
-
static serverUnavailable(fetchOptions: FetchOptions, cause: any): FetchException {
|
|
172
|
-
const protocolPattern = /^[a-z]+:\/\//i,
|
|
173
|
-
originPattern = /^[a-z]+:\/\/[^/]+/i,
|
|
174
|
-
match = fetchOptions.url.match(originPattern),
|
|
175
|
-
origin = match
|
|
176
|
-
? match[0]
|
|
177
|
-
: protocolPattern.test(XH.baseUrl)
|
|
178
|
-
? XH.baseUrl
|
|
179
|
-
: window.location.origin;
|
|
180
|
-
|
|
181
|
-
return this.createFetchException({
|
|
182
|
-
name: 'Server Unavailable',
|
|
183
|
-
message: `Unable to contact the server at ${origin}`,
|
|
184
|
-
isServerUnavailable: true,
|
|
185
|
-
fetchOptions,
|
|
186
|
-
cause
|
|
187
|
-
});
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
//-----------------------
|
|
191
|
-
// Implementation
|
|
192
|
-
//-----------------------
|
|
193
|
-
private static createFetchException(attributes: PlainObject) {
|
|
194
|
-
let correlationId: string = null;
|
|
195
|
-
const correlationIdHeaderKey = XH?.fetchService?.correlationIdHeaderKey;
|
|
196
|
-
if (correlationIdHeaderKey) {
|
|
197
|
-
correlationId = attributes.fetchOptions?.headers?.[correlationIdHeaderKey];
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
return this.createInternal({
|
|
201
|
-
isFetchAborted: false,
|
|
202
|
-
httpStatus: 0, // native fetch doesn't put status on its Error
|
|
203
|
-
serverDetails: null,
|
|
204
|
-
stack: null, // server-sourced exceptions do not include, neither should client, not relevant
|
|
205
|
-
correlationId,
|
|
206
|
-
...attributes
|
|
207
|
-
}) as FetchException;
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
private static createInternal(attributes: PlainObject, baseError?: Error) {
|
|
211
|
-
const {message, ...rest} = attributes;
|
|
212
|
-
const ret = Object.assign(
|
|
213
|
-
baseError ?? new Error(message),
|
|
214
|
-
{
|
|
215
|
-
isRoutine: false,
|
|
216
|
-
isHoistException: true
|
|
217
|
-
},
|
|
218
|
-
rest
|
|
219
|
-
) as HoistException;
|
|
220
|
-
|
|
221
|
-
// statuses of 0, 4XX, 5XX are server errors, so stack irrelevant and potentially misleading
|
|
222
|
-
if (ret.stack == null || /^[045]/.test(ret.httpStatus)) delete ret.stack;
|
|
223
|
-
|
|
224
|
-
return ret;
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
function safeParseJson(txt: string): PlainObject {
|
|
229
|
-
try {
|
|
230
|
-
return JSON.parse(txt);
|
|
231
|
-
} catch (ignored) {
|
|
232
|
-
return null;
|
|
233
|
-
}
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
function extractMessage(parsedResp: PlainObject, responseText: string, statusText: string): string {
|
|
237
|
-
let ret: string;
|
|
238
|
-
if (parsedResp) {
|
|
239
|
-
// From parsed response, including cause if provided (e.g. ExternalHttpException)
|
|
240
|
-
ret = parsedResp.message;
|
|
241
|
-
if (isString(parsedResp.cause)) {
|
|
242
|
-
const cause = truncate(parsedResp.cause, {length: 255});
|
|
243
|
-
ret = ret ? `${ret} (Caused by: ${cause})` : cause;
|
|
244
|
-
}
|
|
245
|
-
} else {
|
|
246
|
-
// Use raw text if not JSON parseable
|
|
247
|
-
ret = truncate(responseText?.trim(), {length: 255});
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
// Fallback to statusText if we have nothing else.
|
|
251
|
-
return ret || statusText;
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
export function isHoistException(src: unknown): src is HoistException {
|
|
255
|
-
return src?.['isHoistException'];
|
|
256
|
-
}
|
package/utils/version/index.ts
DELETED
|
File without changes
|
|
File without changes
|