@xh/hoist 76.0.0-SNAPSHOT.1758217044052 → 76.0.0-SNAPSHOT.1758310971360
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 +5 -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 +10 -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 +3 -67
- package/utils/{log → js}/LogUtils.ts +78 -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,
|
|
@@ -121,25 +121,7 @@ export function isJSON(obj: any): boolean {
|
|
|
121
121
|
*/
|
|
122
122
|
export function throwIf(condition: any, message: unknown) {
|
|
123
123
|
if (condition) {
|
|
124
|
-
throw Exception.create(message);
|
|
125
|
-
}
|
|
126
|
-
}
|
|
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);
|
|
124
|
+
throw Exception.create(message); // low-level exception api for low-level package
|
|
143
125
|
}
|
|
144
126
|
}
|
|
145
127
|
|
|
@@ -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,59 @@ 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
|
+
// low-level exception api for low-level package
|
|
172
|
+
throw Exception.create(`${src}The use of '${name}' is no longer supported.${msg}`);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Document and warn on usage of a deprecated API
|
|
177
|
+
*
|
|
178
|
+
* @param name - the name of the deprecated parameter
|
|
179
|
+
*/
|
|
180
|
+
const _seenWarnings = {};
|
|
181
|
+
export function apiDeprecated(name: string, opts: APIWarnOptions = {}) {
|
|
182
|
+
if ('test' in opts && isUndefined(opts.test)) return;
|
|
183
|
+
|
|
184
|
+
const v = opts.v ?? 'a future release',
|
|
185
|
+
msg = opts.msg ?? '',
|
|
186
|
+
warn = `The use of '${name}' has been deprecated and will be removed in ${v}. ${msg}`;
|
|
187
|
+
if (!_seenWarnings[warn]) {
|
|
188
|
+
logWarn(warn, opts.source);
|
|
189
|
+
_seenWarnings[warn] = true;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
124
193
|
//----------------------------------
|
|
125
194
|
// Implementation
|
|
126
195
|
//----------------------------------
|
|
127
196
|
function loggedDo<T>(messages: Some<unknown>, fn: () => T, source: LogSource, level: LogLevel): T {
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
if (_severity[level] < _severity[getLogLevel()]) {
|
|
197
|
+
if (_severity[level] < _severity[_logLevel]) {
|
|
131
198
|
return fn?.();
|
|
132
199
|
}
|
|
133
200
|
|
|
@@ -202,19 +269,11 @@ function parseSource(source: LogSource): string {
|
|
|
202
269
|
return null;
|
|
203
270
|
}
|
|
204
271
|
|
|
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
272
|
//----------------------------------------------------------------
|
|
215
273
|
// Initialization + Level/Severity support.
|
|
216
274
|
// Initialize during parsing to make available immediately.
|
|
217
275
|
//----------------------------------------------------------------
|
|
218
276
|
let _logLevel: LogLevel = 'info';
|
|
277
|
+
const _severity: Record<LogLevel, number> = {error: 3, warn: 2, info: 1, debug: 0};
|
|
219
278
|
|
|
220
279
|
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
|