@xh/hoist 73.0.0-SNAPSHOT.1745446674015 → 73.0.0-SNAPSHOT.1745589992773

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.
Files changed (45) hide show
  1. package/CHANGELOG.md +17 -5
  2. package/admin/AppModel.ts +19 -12
  3. package/admin/columns/Core.ts +29 -0
  4. package/admin/columns/Tracking.ts +3 -23
  5. package/admin/tabs/BaseAdminTabModel.ts +17 -0
  6. package/admin/tabs/activity/{ActivityTab.scss → tracking/ActivityTracking.scss} +0 -8
  7. package/admin/tabs/activity/tracking/ActivityTrackingPanel.ts +1 -0
  8. package/admin/tabs/activity/tracking/detail/ActivityDetailView.ts +5 -1
  9. package/admin/tabs/{activity/ActivityTab.ts → client/ClientTab.ts} +7 -8
  10. package/admin/tabs/{cluster/instances/websocket/WebSocketColumns.ts → client/clients/ClientsColumns.ts} +12 -6
  11. package/admin/tabs/{cluster/instances/websocket/WebSocketModel.ts → client/clients/ClientsModel.ts} +99 -57
  12. package/admin/tabs/{cluster/instances/websocket/WebSocketPanel.ts → client/clients/ClientsPanel.ts} +32 -26
  13. package/admin/tabs/{activity/clienterrors → client/errors}/ClientErrorDetail.ts +9 -5
  14. package/admin/tabs/client/errors/ClientErrors.scss +52 -0
  15. package/admin/tabs/{activity/clienterrors → client/errors}/ClientErrorsPanel.ts +2 -1
  16. package/admin/tabs/cluster/instances/BaseInstanceModel.ts +3 -10
  17. package/admin/tabs/cluster/instances/InstancesTabModel.ts +1 -3
  18. package/admin/tabs/monitor/MonitorTabModel.ts +4 -3
  19. package/admin/tabs/userData/roles/RoleModel.ts +1 -0
  20. package/admin/tabs/userData/roles/RolePanel.ts +4 -2
  21. package/build/types/admin/columns/Core.d.ts +3 -0
  22. package/build/types/admin/tabs/BaseAdminTabModel.d.ts +6 -0
  23. package/build/types/admin/tabs/activity/tracking/ActivityTrackingPanel.d.ts +1 -0
  24. package/build/types/admin/tabs/client/ClientTab.d.ts +1 -0
  25. package/build/types/admin/tabs/{cluster/instances/websocket/WebSocketModel.d.ts → client/clients/ClientsModel.d.ts} +5 -2
  26. package/build/types/admin/tabs/client/clients/ClientsPanel.d.ts +2 -0
  27. package/build/types/admin/tabs/{activity/clienterrors → client/errors}/ClientErrorsPanel.d.ts +1 -0
  28. package/build/types/admin/tabs/cluster/instances/BaseInstanceModel.d.ts +3 -5
  29. package/build/types/admin/tabs/monitor/MonitorTabModel.d.ts +3 -2
  30. package/build/types/core/types/Interfaces.d.ts +1 -1
  31. package/build/types/promise/Promise.d.ts +2 -2
  32. package/build/types/svc/TrackService.d.ts +1 -1
  33. package/core/types/Interfaces.ts +1 -1
  34. package/package.json +1 -1
  35. package/promise/Promise.ts +55 -32
  36. package/svc/TrackService.ts +1 -1
  37. package/tsconfig.tsbuildinfo +1 -1
  38. package/build/types/admin/tabs/activity/ActivityTab.d.ts +0 -2
  39. package/build/types/admin/tabs/cluster/instances/websocket/WebSocketPanel.d.ts +0 -2
  40. /package/admin/tabs/{activity/clienterrors → client/errors}/ClientErrorsModel.ts +0 -0
  41. /package/admin/tabs/{activity → client}/feedback/FeedbackPanel.ts +0 -0
  42. /package/build/types/admin/tabs/{cluster/instances/websocket/WebSocketColumns.d.ts → client/clients/ClientsColumns.d.ts} +0 -0
  43. /package/build/types/admin/tabs/{activity/clienterrors → client/errors}/ClientErrorDetail.d.ts +0 -0
  44. /package/build/types/admin/tabs/{activity/clienterrors → client/errors}/ClientErrorsModel.d.ts +0 -0
  45. /package/build/types/admin/tabs/{activity → client}/feedback/FeedbackPanel.d.ts +0 -0
@@ -23,16 +23,19 @@ export const clientErrorDetail = hoistCmp.factory<ClientErrorsModel>(({model}) =
23
23
  if (!selectedRecord) return null;
24
24
 
25
25
  return panel({
26
- className: 'xh-admin-activity-detail',
26
+ className: 'xh-admin-client-errors-detail',
27
+ collapsedTitle: 'Error Details',
28
+ collapsedIcon: Icon.info(),
29
+ compactHeader: true,
27
30
  modelConfig: {
28
31
  side: 'bottom',
29
- defaultSize: 370
32
+ defaultSize: 400
30
33
  },
31
34
  item: form({
32
35
  fieldDefaults: {inline: true, readonlyRenderer: valOrNa},
33
36
  item: hframe(
34
37
  div({
35
- className: 'xh-admin-activity-detail__form',
38
+ className: 'xh-admin-client-errors-detail__form',
36
39
  style: {width: '400px'},
37
40
  items: [
38
41
  h3(Icon.info(), 'Error Info'),
@@ -89,12 +92,13 @@ export const clientErrorDetail = hoistCmp.factory<ClientErrorsModel>(({model}) =
89
92
  className: 'xh-border-left',
90
93
  items: [
91
94
  panel({
92
- height: 100,
95
+ flex: 0.5,
93
96
  className: 'xh-border-bottom',
94
97
  items: [
95
98
  h3(Icon.comment(), 'User Message'),
96
99
  div({
97
- className: `xh-admin-activity-detail__message`,
100
+ className: `xh-pad`,
101
+ style: {overflowY: 'auto'},
98
102
  item: userMsg
99
103
  })
100
104
  ],
@@ -0,0 +1,52 @@
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
+
8
+ .xh-admin-client-errors-detail {
9
+ &__form {
10
+ overflow-y: auto;
11
+
12
+ .xh-form-field {
13
+ margin: 0;
14
+ padding: 2px var(--xh-pad-px);
15
+
16
+ &.xh-form-field-readonly .xh-form-field-readonly-display {
17
+ padding: var(--xh-pad-half-px);
18
+ }
19
+
20
+ &:nth-child(odd) {
21
+ background-color: var(--xh-grid-bg-odd);
22
+ }
23
+ }
24
+
25
+ .xh-form-field-label {
26
+ font-size: var(--xh-font-size-small-px);
27
+ padding: 0;
28
+ }
29
+ }
30
+
31
+ h3 {
32
+ color: var(--xh-text-color-headings);
33
+ background-color: var(--xh-blue-gray-light);
34
+ margin: 0 !important;
35
+ font-size: var(--xh-font-size-px);
36
+ font-weight: normal;
37
+ padding: var(--xh-pad-half-px) !important;
38
+ border-bottom: var(--xh-border-solid);
39
+
40
+ &:not(:first-child) {
41
+ border-top: var(--xh-border-solid);
42
+ }
43
+
44
+ .xh-dark & {
45
+ background-color: var(--xh-blue-gray-dark);
46
+ }
47
+
48
+ .xh-icon {
49
+ margin-right: var(--xh-pad-px);
50
+ }
51
+ }
52
+ }
@@ -16,13 +16,14 @@ import {Icon} from '@xh/hoist/icon';
16
16
  import {LocalDate} from '@xh/hoist/utils/datetime';
17
17
  import {clientErrorDetail} from './ClientErrorDetail';
18
18
  import {ClientErrorsModel} from './ClientErrorsModel';
19
+ import './ClientErrors.scss';
19
20
 
20
21
  export const clientErrorsPanel = hoistCmp.factory({
21
22
  model: creates(ClientErrorsModel),
22
23
 
23
24
  render() {
24
25
  return panel({
25
- className: 'xh-admin-activity-panel',
26
+ className: 'xh-admin-client-errors-panel',
26
27
  tbar: tbar(),
27
28
  items: [grid(), clientErrorDetail()],
28
29
  mask: 'onLoad'
@@ -5,13 +5,10 @@
5
5
  * Copyright © 2025 Extremely Heavy Industries Inc.
6
6
  */
7
7
  import {InstancesTabModel} from '@xh/hoist/admin/tabs/cluster/instances/InstancesTabModel';
8
- import {HoistModel, LoadSpec, lookup, XH} from '@xh/hoist/core';
9
- import {createRef} from 'react';
10
- import {isDisplayed} from '@xh/hoist/utils/js';
11
-
12
- export class BaseInstanceModel extends HoistModel {
13
- viewRef = createRef<HTMLElement>();
8
+ import {LoadSpec, lookup, XH} from '@xh/hoist/core';
9
+ import {BaseAdminTabModel} from '@xh/hoist/admin/tabs/BaseAdminTabModel';
14
10
 
11
+ export class BaseInstanceModel extends BaseAdminTabModel {
15
12
  @lookup(() => InstancesTabModel) parent: InstancesTabModel;
16
13
 
17
14
  get instanceName(): string {
@@ -30,10 +27,6 @@ export class BaseInstanceModel extends HoistModel {
30
27
  });
31
28
  }
32
29
 
33
- get isVisible() {
34
- return isDisplayed(this.viewRef.current);
35
- }
36
-
37
30
  //-------------------
38
31
  // Implementation
39
32
  //-------------------
@@ -15,7 +15,6 @@ import {
15
15
  } from '@xh/hoist/admin/tabs/cluster/instances/memory/MemoryMonitorModel';
16
16
  import {memoryMonitorPanel} from '@xh/hoist/admin/tabs/cluster/instances/memory/MemoryMonitorPanel';
17
17
  import {servicePanel} from '@xh/hoist/admin/tabs/cluster/instances/services/ServicePanel';
18
- import {webSocketPanel} from '@xh/hoist/admin/tabs/cluster/instances/websocket/WebSocketPanel';
19
18
  import {badge} from '@xh/hoist/cmp/badge';
20
19
  import {GridContextMenuSpec, GridModel, numberCol} from '@xh/hoist/cmp/grid';
21
20
  import {hbox} from '@xh/hoist/cmp/layout';
@@ -205,8 +204,7 @@ export class InstancesTabModel extends HoistModel {
205
204
  content: connPoolMonitorPanel
206
205
  },
207
206
  {id: 'environment', icon: Icon.globe(), content: serverEnvPanel},
208
- {id: 'services', icon: Icon.gears(), content: servicePanel},
209
- {id: 'webSockets', title: 'WebSockets', icon: Icon.bolt(), content: webSocketPanel}
207
+ {id: 'services', icon: Icon.gears(), content: servicePanel}
210
208
  ]
211
209
  });
212
210
  }
@@ -5,15 +5,16 @@
5
5
  * Copyright © 2025 Extremely Heavy Industries Inc.
6
6
  */
7
7
  import {MonitorResults, MonitorStatus} from '@xh/hoist/admin/tabs/monitor/Types';
8
- import {HoistModel, LoadSpec, managed, persist, XH} from '@xh/hoist/core';
8
+ import {LoadSpec, managed, persist, XH} from '@xh/hoist/core';
9
9
  import {Icon} from '@xh/hoist/icon';
10
10
  import {action, bindable, computed, makeObservable, observable} from '@xh/hoist/mobx';
11
11
  import {Timer} from '@xh/hoist/utils/async';
12
12
  import {SECONDS} from '@xh/hoist/utils/datetime';
13
13
  import {pluralize} from '@xh/hoist/utils/js';
14
14
  import {filter, isEqual, minBy, sortBy} from 'lodash';
15
+ import {BaseAdminTabModel} from '@xh/hoist/admin/tabs/BaseAdminTabModel';
15
16
 
16
- export class MonitorTabModel extends HoistModel {
17
+ export class MonitorTabModel extends BaseAdminTabModel {
17
18
  override persistWith = {localStorageKey: 'xhAdminClientMonitorState'};
18
19
 
19
20
  @observable.ref results: MonitorResults[] = [];
@@ -73,7 +74,7 @@ export class MonitorTabModel extends HoistModel {
73
74
  }
74
75
 
75
76
  override async doLoadAsync(loadSpec: LoadSpec) {
76
- if (!XH.pageIsVisible) return;
77
+ if (!this.isVisible) return;
77
78
 
78
79
  try {
79
80
  const results = await XH.fetchJson({url: 'monitorResultsAdmin/results', loadSpec});
@@ -328,6 +328,7 @@ export class RoleModel extends HoistModel {
328
328
  {
329
329
  ...actionCol,
330
330
  actionsShowOnHoverOnly: true,
331
+ excludeFromChooser: true,
331
332
  width: calcActionColWidth(1),
332
333
  actions: [this.editAction()],
333
334
  omit: this.readonly
@@ -8,7 +8,7 @@ import {recategorizeDialog} from '@xh/hoist/admin/tabs/userData/roles/recategori
8
8
  import {grid} from '@xh/hoist/cmp/grid';
9
9
  import {fragment, hframe, vframe} from '@xh/hoist/cmp/layout';
10
10
  import {creates, hoistCmp} from '@xh/hoist/core';
11
- import {button} from '@xh/hoist/desktop/cmp/button';
11
+ import {button, colChooserButton} from '@xh/hoist/desktop/cmp/button';
12
12
  import {errorMessage} from '@xh/hoist/cmp/error';
13
13
  import {filterChooser} from '@xh/hoist/desktop/cmp/filter';
14
14
  import {switchInput} from '@xh/hoist/desktop/cmp/input';
@@ -50,7 +50,9 @@ export const rolePanel = hoistCmp.factory({
50
50
  bind: 'showInGroups',
51
51
  label: 'Group by Category',
52
52
  labelSide: 'left'
53
- })
53
+ }),
54
+ '-',
55
+ colChooserButton()
54
56
  ],
55
57
  item: hframe(vframe(grid(), roleGraph()), detailsPanel())
56
58
  }),
@@ -1,3 +1,4 @@
1
+ /// <reference types="react" />
1
2
  import { ColumnSpec } from '@xh/hoist/cmp/grid';
2
3
  export declare const name: ColumnSpec;
3
4
  export declare const type: ColumnSpec;
@@ -5,3 +6,5 @@ export declare const description: ColumnSpec;
5
6
  export declare const notes: ColumnSpec;
6
7
  export declare const note: ColumnSpec;
7
8
  export declare const timestampNoYear: ColumnSpec;
9
+ export declare function badgeRenderer(v: any): "-" | import("react").ReactElement<import("@xh/hoist/cmp/badge").BadgeProps, any>;
10
+ export declare const badgeCol: ColumnSpec;
@@ -0,0 +1,6 @@
1
+ /// <reference types="react" />
2
+ import { HoistModel } from '@xh/hoist/core';
3
+ export declare class BaseAdminTabModel extends HoistModel {
4
+ viewRef: import("react").RefObject<HTMLElement>;
5
+ get isVisible(): boolean;
6
+ }
@@ -1,2 +1,3 @@
1
1
  import { ActivityTrackingModel } from './ActivityTrackingModel';
2
+ import './ActivityTracking.scss';
2
3
  export declare const activityTrackingPanel: import("@xh/hoist/core").ElementFactory<import("@xh/hoist/core").DefaultHoistProps<ActivityTrackingModel>>;
@@ -0,0 +1 @@
1
+ export declare const clientTab: import("@xh/hoist/core").ElementFactory<import("@xh/hoist/core").DefaultHoistProps<import("@xh/hoist/core").HoistModel>>;
@@ -1,10 +1,11 @@
1
- import { BaseInstanceModel } from '@xh/hoist/admin/tabs/cluster/instances/BaseInstanceModel';
2
1
  import { GridModel } from '@xh/hoist/cmp/grid';
3
2
  import { LoadSpec } from '@xh/hoist/core';
4
3
  import { RecordActionSpec, StoreRecord } from '@xh/hoist/data';
5
- export declare class WebSocketModel extends BaseInstanceModel {
4
+ import { BaseAdminTabModel } from '@xh/hoist/admin/tabs/BaseAdminTabModel';
5
+ export declare class ClientsModel extends BaseAdminTabModel {
6
6
  lastRefresh: number;
7
7
  gridModel: GridModel;
8
+ groupBy: 'user' | 'instance';
8
9
  private _timer;
9
10
  forceSuspendAction: RecordActionSpec;
10
11
  reqHealthReportAction: RecordActionSpec;
@@ -12,5 +13,7 @@ export declare class WebSocketModel extends BaseInstanceModel {
12
13
  doLoadAsync(loadSpec: LoadSpec): Promise<void>;
13
14
  forceSuspendAsync(toRecs: StoreRecord[]): Promise<void>;
14
15
  requestHealthReportAsync(toRecs: StoreRecord[]): Promise<void>;
16
+ private createGridModel;
15
17
  private bulkPush;
18
+ private applyGroupBy;
16
19
  }
@@ -0,0 +1,2 @@
1
+ import { ClientsModel } from './ClientsModel';
2
+ export declare const clientsPanel: import("@xh/hoist/core").ElementFactory<import("@xh/hoist/core").DefaultHoistProps<ClientsModel>>;
@@ -1,2 +1,3 @@
1
1
  import { ClientErrorsModel } from './ClientErrorsModel';
2
+ import './ClientErrors.scss';
2
3
  export declare const clientErrorsPanel: import("@xh/hoist/core").ElementFactory<import("@xh/hoist/core").DefaultHoistProps<ClientErrorsModel>>;
@@ -1,11 +1,9 @@
1
- /// <reference types="react" />
2
1
  import { InstancesTabModel } from '@xh/hoist/admin/tabs/cluster/instances/InstancesTabModel';
3
- import { HoistModel, LoadSpec } from '@xh/hoist/core';
4
- export declare class BaseInstanceModel extends HoistModel {
5
- viewRef: import("react").RefObject<HTMLElement>;
2
+ import { LoadSpec } from '@xh/hoist/core';
3
+ import { BaseAdminTabModel } from '@xh/hoist/admin/tabs/BaseAdminTabModel';
4
+ export declare class BaseInstanceModel extends BaseAdminTabModel {
6
5
  parent: InstancesTabModel;
7
6
  get instanceName(): string;
8
7
  handleLoadException(e: unknown, loadSpec: LoadSpec): void;
9
- get isVisible(): boolean;
10
8
  private isInstanceNotFound;
11
9
  }
@@ -1,7 +1,8 @@
1
1
  import { MonitorResults, MonitorStatus } from '@xh/hoist/admin/tabs/monitor/Types';
2
- import { HoistModel, LoadSpec } from '@xh/hoist/core';
2
+ import { LoadSpec } from '@xh/hoist/core';
3
3
  import { Timer } from '@xh/hoist/utils/async';
4
- export declare class MonitorTabModel extends HoistModel {
4
+ import { BaseAdminTabModel } from '@xh/hoist/admin/tabs/BaseAdminTabModel';
5
+ export declare class MonitorTabModel extends BaseAdminTabModel {
5
6
  persistWith: {
6
7
  localStorageKey: string;
7
8
  };
@@ -165,7 +165,7 @@ export interface AppOptionSpec {
165
165
  /**
166
166
  * Severity levels for tracking. Default is 'INFO'.
167
167
  */
168
- export type TrackSeverity = 'DEBUG' | 'INFO' | 'WARN';
168
+ export type TrackSeverity = 'DEBUG' | 'INFO' | 'WARN' | 'ERROR';
169
169
  /**
170
170
  * Options for tracking activity on the server via TrackService.
171
171
  */
@@ -18,7 +18,7 @@ declare global {
18
18
  * selector will be handled by this method.
19
19
  * @param fn - catch handler
20
20
  */
21
- catchWhen<TResult = undefined>(selector: ((e: any) => boolean) | Some<string>, fn?: (reason: any) => Awaitable<TResult>): Promise<T | TResult>;
21
+ catchWhen<TResult = undefined>(selector: ((e: unknown) => boolean) | Some<string>, fn?: (reason: unknown) => Awaitable<TResult>): Promise<T | TResult>;
22
22
  /**
23
23
  * Version of `catch()` that passes the error onto Hoist's default exception handler for
24
24
  * convention-driven logging and alerting. Typically called last in a Promise chain.
@@ -27,7 +27,7 @@ declare global {
27
27
  /**
28
28
  * Version of `catchDefault()` that will only catch certain exceptions.
29
29
  */
30
- catchDefaultWhen(selector: ((e: any) => boolean) | Some<string>, options: ExceptionHandlerOptions): Promise<T | undefined>;
30
+ catchDefaultWhen(selector: ((e: unknown) => boolean) | Some<string>, options: ExceptionHandlerOptions): Promise<T | undefined>;
31
31
  /**
32
32
  * Wait on a potentially async function before passing on the original value.
33
33
  * Useful when we want to block and do something on the promise chain, but do not want to
@@ -36,7 +36,7 @@ interface ActivityTrackingConfig {
36
36
  levels?: Array<{
37
37
  username: string | '*';
38
38
  category: string | '*';
39
- severity: 'DEBUG' | 'INFO' | 'WARN';
39
+ severity: 'DEBUG' | 'INFO' | 'WARN' | 'ERROR';
40
40
  }>;
41
41
  }
42
42
  export {};
@@ -207,7 +207,7 @@ export interface AppOptionSpec {
207
207
  /**
208
208
  * Severity levels for tracking. Default is 'INFO'.
209
209
  */
210
- export type TrackSeverity = 'DEBUG' | 'INFO' | 'WARN';
210
+ export type TrackSeverity = 'DEBUG' | 'INFO' | 'WARN' | 'ERROR';
211
211
 
212
212
  /**
213
213
  * Options for tracking activity on the server via TrackService.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xh/hoist",
3
- "version": "73.0.0-SNAPSHOT.1745446674015",
3
+ "version": "73.0.0-SNAPSHOT.1745589992773",
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",
@@ -12,11 +12,13 @@ import {
12
12
  TrackOptions,
13
13
  XH,
14
14
  Some,
15
- Awaitable
15
+ Awaitable,
16
+ TimeoutExceptionConfig
16
17
  } from '@xh/hoist/core';
17
18
  import {action} from '@xh/hoist/mobx';
18
19
  import {olderThan, SECONDS} from '@xh/hoist/utils/datetime';
19
20
  import {castArray, isFunction, isNumber, isString} from 'lodash';
21
+ import {isOmitted} from '@xh/hoist/utils/impl';
20
22
 
21
23
  /**
22
24
  * Enhancements to the Global Promise object.
@@ -39,8 +41,8 @@ declare global {
39
41
  * @param fn - catch handler
40
42
  */
41
43
  catchWhen<TResult = undefined>(
42
- selector: ((e: any) => boolean) | Some<string>,
43
- fn?: (reason: any) => Awaitable<TResult>
44
+ selector: ((e: unknown) => boolean) | Some<string>,
45
+ fn?: (reason: unknown) => Awaitable<TResult>
44
46
  ): Promise<T | TResult>;
45
47
 
46
48
  /**
@@ -53,7 +55,7 @@ declare global {
53
55
  * Version of `catchDefault()` that will only catch certain exceptions.
54
56
  */
55
57
  catchDefaultWhen(
56
- selector: ((e: any) => boolean) | Some<string>,
58
+ selector: ((e: unknown) => boolean) | Some<string>,
57
59
  options: ExceptionHandlerOptions
58
60
  ): Promise<T | undefined>;
59
61
 
@@ -169,43 +171,63 @@ export function never<T>(): Promise<T> {
169
171
  //--------------------------------
170
172
  const enhancePromise = promisePrototype => {
171
173
  Object.assign(promisePrototype, {
172
- thenAction(fn) {
174
+ thenAction<T, TResult>(fn: (value: T) => Awaitable<TResult>): Promise<TResult> {
173
175
  return this.then(action(fn));
174
176
  },
175
177
 
176
- catchWhen(selector, fn) {
177
- return this.catch(e => {
178
+ catchWhen<T, TResult = undefined>(
179
+ selector: ((e: unknown) => boolean) | Some<string>,
180
+ fn?: (reason: unknown) => Awaitable<TResult>
181
+ ): Promise<T | TResult> {
182
+ return this.catch((e: unknown) => {
178
183
  this.throwIfFailsSelector(e, selector);
179
184
  return fn ? fn(e) : undefined;
180
185
  });
181
186
  },
182
187
 
183
- catchDefault(options) {
184
- return this.catch(e => XH.handleException(e, options));
188
+ catchDefault<T>(options?: ExceptionHandlerOptions): Promise<T | undefined> {
189
+ return this.catch((e: unknown) => XH.handleException(e, options));
185
190
  },
186
191
 
187
- catchDefaultWhen(selector, options) {
188
- return this.catch(e => {
192
+ catchDefaultWhen<T>(
193
+ selector: ((e: unknown) => boolean) | Some<string>,
194
+ options: ExceptionHandlerOptions
195
+ ): Promise<T | undefined> {
196
+ return this.catch((e: unknown) => {
189
197
  this.throwIfFailsSelector(e, selector);
190
198
  return XH.handleException(e, options);
191
199
  });
192
200
  },
193
201
 
194
- track(options) {
195
- if (!options || (isFunction(options.omit) ? options.omit() : options.omit)) return this;
196
- if (isString(options)) options = {message: options};
197
-
198
- const startTime = Date.now();
199
- return this.finally(() => {
200
- XH.track({
201
- timestamp: startTime,
202
- elapsed: Date.now() - startTime,
203
- ...options
204
- });
205
- });
202
+ track<T>(options: TrackOptions | string): Promise<T> {
203
+ if (!options) return this;
204
+
205
+ const startTime = Date.now(),
206
+ doTrack = (isError: boolean) => {
207
+ const opts: TrackOptions = isString(options)
208
+ ? {message: options}
209
+ : {...options};
210
+
211
+ opts.timestamp = startTime;
212
+ opts.elapsed = Date.now() - startTime;
213
+ if (isError) opts.severity = 'ERROR';
214
+
215
+ XH.track(opts);
216
+ };
217
+
218
+ return this.then(
219
+ (v: T) => {
220
+ doTrack(true);
221
+ return v;
222
+ },
223
+ (t: unknown) => {
224
+ doTrack(false);
225
+ throw t;
226
+ }
227
+ );
206
228
  },
207
229
 
208
- tap(onFulfillment) {
230
+ tap<T>(onFulfillment: (value: T) => any): Promise<T> {
209
231
  let ret = null;
210
232
  const resolveFn = data => {
211
233
  ret = data;
@@ -215,13 +237,14 @@ const enhancePromise = promisePrototype => {
215
237
  return this.then(resolveFn).then(() => ret);
216
238
  },
217
239
 
218
- wait(interval) {
240
+ wait<T>(interval: number): Promise<T> {
219
241
  return this.finally(() => wait(interval));
220
242
  },
221
243
 
222
- timeout(config) {
223
- if (config == null) return this;
224
- if (isNumber(config)) config = {interval: config};
244
+ timeout<T>(spec: PromiseTimeoutSpec): Promise<T> {
245
+ if (spec == null) return this;
246
+
247
+ const config: TimeoutExceptionConfig = isNumber(spec) ? {interval: spec} : spec;
225
248
  const interval = config.interval;
226
249
 
227
250
  let completed = false;
@@ -236,14 +259,14 @@ const enhancePromise = promisePrototype => {
236
259
  return Promise.race([deadline, promise]);
237
260
  },
238
261
 
239
- linkTo(cfg) {
262
+ linkTo<T>(cfg: PromiseLinkSpec): Promise<T> {
240
263
  if (!cfg) return this;
241
264
 
242
- if (cfg.isTaskObserver) {
265
+ if (cfg instanceof TaskObserver) {
243
266
  cfg = {observer: cfg};
244
267
  }
245
268
 
246
- if (cfg.observer && !(isFunction(cfg.omit) ? cfg.omit() : cfg.omit)) {
269
+ if (cfg.observer && !isOmitted(cfg)) {
247
270
  cfg.observer.linkTo(TaskObserver.forPromise({promise: this, message: cfg.message}));
248
271
  }
249
272
  return this;
@@ -252,7 +275,7 @@ const enhancePromise = promisePrototype => {
252
275
  //--------------------------------
253
276
  // Implementation
254
277
  //--------------------------------
255
- throwIfFailsSelector(e, selector) {
278
+ throwIfFailsSelector(e: any, selector: any) {
256
279
  const fn = isFunction(selector) ? selector : e => castArray(selector).includes(e.name);
257
280
  if (!fn(e)) throw e;
258
281
  }
@@ -168,6 +168,6 @@ interface ActivityTrackingConfig {
168
168
  levels?: Array<{
169
169
  username: string | '*';
170
170
  category: string | '*';
171
- severity: 'DEBUG' | 'INFO' | 'WARN';
171
+ severity: 'DEBUG' | 'INFO' | 'WARN' | 'ERROR';
172
172
  }>;
173
173
  }