@equinor/fusion-framework-module-widget 16.0.2 → 16.0.4

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 (54) hide show
  1. package/CHANGELOG.md +51 -0
  2. package/dist/esm/Widget.js +13 -2
  3. package/dist/esm/Widget.js.map +1 -1
  4. package/dist/esm/WidgetManifestLoadError.js +42 -0
  5. package/dist/esm/WidgetManifestLoadError.js.map +1 -0
  6. package/dist/esm/WidgetModuleConfigurator.js +2 -0
  7. package/dist/esm/WidgetModuleConfigurator.js.map +1 -1
  8. package/dist/esm/WidgetModuleProvider.js +12 -3
  9. package/dist/esm/WidgetModuleProvider.js.map +1 -1
  10. package/dist/esm/enable-widget-module.js +1 -1
  11. package/dist/esm/enable-widget-module.js.map +1 -1
  12. package/dist/esm/errors/WidgetConfigLoadError.js +40 -0
  13. package/dist/esm/errors/WidgetConfigLoadError.js.map +1 -0
  14. package/dist/esm/errors/WidgetScriptModuleError.js +17 -0
  15. package/dist/esm/errors/WidgetScriptModuleError.js.map +1 -0
  16. package/dist/esm/module.js +2 -0
  17. package/dist/esm/module.js.map +1 -1
  18. package/dist/esm/state/actions.js +1 -1
  19. package/dist/esm/state/actions.js.map +1 -1
  20. package/dist/esm/state/create-reducer.js +5 -1
  21. package/dist/esm/state/create-reducer.js.map +1 -1
  22. package/dist/esm/state/flows.js +32 -7
  23. package/dist/esm/state/flows.js.map +1 -1
  24. package/dist/esm/utils.js +6 -1
  25. package/dist/esm/utils.js.map +1 -1
  26. package/dist/esm/version.js +1 -1
  27. package/dist/tsconfig.tsbuildinfo +1 -1
  28. package/dist/types/Widget.d.ts +5 -1
  29. package/dist/types/WidgetManifestLoadError.d.ts +36 -0
  30. package/dist/types/WidgetModuleProvider.d.ts +2 -0
  31. package/dist/types/errors/WidgetConfigLoadError.d.ts +27 -0
  32. package/dist/types/errors/WidgetScriptModuleError.d.ts +13 -0
  33. package/dist/types/state/actions.d.ts +8 -8
  34. package/dist/types/state/create-reducer.d.ts +36 -36
  35. package/dist/types/version.d.ts +1 -1
  36. package/package.json +13 -13
  37. package/src/Widget.ts +13 -2
  38. package/src/WidgetManifestLoadError.ts +61 -0
  39. package/src/WidgetModuleConfigurator.ts +2 -0
  40. package/src/WidgetModuleProvider.ts +8 -1
  41. package/src/enable-widget-module.ts +1 -1
  42. package/src/errors/WidgetConfigLoadError.ts +51 -0
  43. package/src/errors/WidgetScriptModuleError.ts +20 -0
  44. package/src/module.ts +2 -0
  45. package/src/state/actions.ts +1 -1
  46. package/src/state/create-reducer.ts +3 -0
  47. package/src/state/flows.ts +47 -21
  48. package/src/types.ts +2 -2
  49. package/src/utils.ts +6 -1
  50. package/src/version.ts +1 -1
  51. package/dist/esm/errors.js +0 -93
  52. package/dist/esm/errors.js.map +0 -1
  53. package/dist/types/errors.d.ts +0 -73
  54. package/src/errors.ts +0 -125
@@ -32,7 +32,11 @@ export declare class Widget {
32
32
  config?: WidgetModuleConfig;
33
33
  /** Optional version or tag parameters forwarded to manifest/config endpoints. */
34
34
  widgetPrams?: GetWidgetParameters['args'];
35
- /** Current snapshot of the widget's internal state (manifest, config, modules, status). */
35
+ /**
36
+ * Current snapshot of the widget's internal state (manifest, config, modules, status).
37
+ *
38
+ * @returns The current {@link WidgetState}.
39
+ */
36
40
  get state(): WidgetState;
37
41
  /**
38
42
  * Constructs a new `Widget` instance.
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Discriminator for categorizing widget HTTP errors.
3
+ *
4
+ * - `'not_found'` — HTTP 404
5
+ * - `'unauthorized'` — HTTP 401
6
+ * - `'unknown'` — any other error
7
+ */
8
+ export type WidgetErrorType = 'not_found' | 'unauthorized' | 'unknown';
9
+ /**
10
+ * Error thrown when a widget manifest cannot be loaded from the backend API.
11
+ *
12
+ * Use the static {@link fromHttpResponse} factory to create instances from
13
+ * HTTP responses with appropriate type mapping.
14
+ */
15
+ export declare class WidgetManifestLoadError extends Error {
16
+ readonly type: WidgetErrorType;
17
+ /**
18
+ * Creates a `WidgetManifestLoadError` from an HTTP `Response`.
19
+ *
20
+ * Maps HTTP 401 to `'unauthorized'`, 404 to `'not_found'`, and all other
21
+ * status codes to `'unknown'`.
22
+ *
23
+ * @param response - The failing HTTP response.
24
+ * @param options - Standard `ErrorOptions` (e.g., `cause`).
25
+ * @returns A typed `WidgetManifestLoadError`.
26
+ */
27
+ static fromHttpResponse(response: Response, options?: ErrorOptions): WidgetManifestLoadError;
28
+ /**
29
+ * @param type - Error category discriminator.
30
+ * @param message - Human-readable error description.
31
+ * @param options - Standard `ErrorOptions` (e.g., `cause`).
32
+ */
33
+ constructor(type: WidgetErrorType, message?: string, options?: ErrorOptions);
34
+ }
35
+ export { WidgetConfigLoadError } from './errors/WidgetConfigLoadError.js';
36
+ export { WidgetScriptModuleError } from './errors/WidgetScriptModuleError.js';
@@ -99,6 +99,7 @@ export declare class WidgetModuleProvider implements IWidgetModuleProvider {
99
99
  * @param widgetKey - Widget identifier.
100
100
  * @param args - Optional version or tag selector.
101
101
  * @returns Observable emitting the {@link WidgetConfig}.
102
+ * @throws {WidgetManifestLoadError} When the underlying query fails.
102
103
  */
103
104
  protected _getWidgetConfig(widgetKey: GetWidgetParameters['widgetKey'], args?: GetWidgetParameters['args']): Observable<WidgetConfig>;
104
105
  /**
@@ -108,6 +109,7 @@ export declare class WidgetModuleProvider implements IWidgetModuleProvider {
108
109
  * @param widgetKey - Widget identifier.
109
110
  * @param args - Optional version or tag selector.
110
111
  * @returns Observable emitting the {@link WidgetManifest}.
112
+ * @throws {WidgetConfigLoadError} When the underlying query fails.
111
113
  */
112
114
  protected _getWidget(widgetKey: GetWidgetParameters['widgetKey'], args?: GetWidgetParameters['args']): Observable<WidgetManifest>;
113
115
  /**
@@ -0,0 +1,27 @@
1
+ import type { WidgetErrorType } from '../WidgetManifestLoadError.js';
2
+ /**
3
+ * Error thrown when a widget configuration cannot be loaded from the backend API.
4
+ *
5
+ * Use the static {@link fromHttpResponse} factory to create instances from
6
+ * HTTP responses with appropriate type mapping.
7
+ */
8
+ export declare class WidgetConfigLoadError extends Error {
9
+ readonly type: WidgetErrorType;
10
+ /**
11
+ * Creates a `WidgetConfigLoadError` from an HTTP `Response`.
12
+ *
13
+ * Maps HTTP 401 to `'unauthorized'`, 404 to `'not_found'`, and all other
14
+ * status codes to `'unknown'`.
15
+ *
16
+ * @param response - The failing HTTP response.
17
+ * @param options - Standard `ErrorOptions` (e.g., `cause`).
18
+ * @returns A typed `WidgetConfigLoadError`.
19
+ */
20
+ static fromHttpResponse(response: Response, options?: ErrorOptions): WidgetConfigLoadError;
21
+ /**
22
+ * @param type - Error category discriminator.
23
+ * @param message - Human-readable error description.
24
+ * @param options - Standard `ErrorOptions` (e.g., `cause`).
25
+ */
26
+ constructor(type: WidgetErrorType, message?: string, options?: ErrorOptions);
27
+ }
@@ -0,0 +1,13 @@
1
+ import type { WidgetErrorType } from '../WidgetManifestLoadError.js';
2
+ /**
3
+ * Error thrown when a widget script module cannot be dynamically imported.
4
+ */
5
+ export declare class WidgetScriptModuleError extends Error {
6
+ readonly type: WidgetErrorType;
7
+ /**
8
+ * @param type - Error category discriminator.
9
+ * @param message - Human-readable error description.
10
+ * @param options - Standard `ErrorOptions` (e.g., `cause`).
11
+ */
12
+ constructor(type: WidgetErrorType, message?: string, options?: ErrorOptions);
13
+ }
@@ -18,10 +18,10 @@ declare const createActions: () => {
18
18
  /** Async action triplet for fetching the widget manifest from the API. */
19
19
  fetchManifest: import("@equinor/fusion-observable").ActionCreatorWithPreparedPayload<[payload: {
20
20
  key: string;
21
- args?: GetWidgetParameters["args"];
21
+ args?: GetWidgetParameters['args'];
22
22
  }, update?: boolean | undefined], {
23
23
  key: string;
24
- args?: GetWidgetParameters["args"];
24
+ args?: GetWidgetParameters['args'];
25
25
  }, "fetch_manifest::request", never, {
26
26
  update: boolean | undefined;
27
27
  }> & {
@@ -33,10 +33,10 @@ declare const createActions: () => {
33
33
  /** Async action triplet for fetching the widget config from the API. */
34
34
  fetchConfig: import("@equinor/fusion-observable").ActionCreatorWithPreparedPayload<[payload: {
35
35
  key: string;
36
- args?: GetWidgetParameters["args"];
36
+ args?: GetWidgetParameters['args'];
37
37
  }, update?: boolean | undefined], {
38
38
  key: string;
39
- args?: GetWidgetParameters["args"];
39
+ args?: GetWidgetParameters['args'];
40
40
  }, "fetch_config::request", never, {
41
41
  update: boolean | undefined;
42
42
  }> & {
@@ -68,10 +68,10 @@ export declare const actions: {
68
68
  /** Async action triplet for fetching the widget manifest from the API. */
69
69
  fetchManifest: import("@equinor/fusion-observable").ActionCreatorWithPreparedPayload<[payload: {
70
70
  key: string;
71
- args?: GetWidgetParameters["args"];
71
+ args?: GetWidgetParameters['args'];
72
72
  }, update?: boolean | undefined], {
73
73
  key: string;
74
- args?: GetWidgetParameters["args"];
74
+ args?: GetWidgetParameters['args'];
75
75
  }, "fetch_manifest::request", never, {
76
76
  update: boolean | undefined;
77
77
  }> & {
@@ -83,10 +83,10 @@ export declare const actions: {
83
83
  /** Async action triplet for fetching the widget config from the API. */
84
84
  fetchConfig: import("@equinor/fusion-observable").ActionCreatorWithPreparedPayload<[payload: {
85
85
  key: string;
86
- args?: GetWidgetParameters["args"];
86
+ args?: GetWidgetParameters['args'];
87
87
  }, update?: boolean | undefined], {
88
88
  key: string;
89
- args?: GetWidgetParameters["args"];
89
+ args?: GetWidgetParameters['args'];
90
90
  }, "fetch_config::request", never, {
91
91
  update: boolean | undefined;
92
92
  }> & {
@@ -9,57 +9,57 @@ import type { WidgetStateInitial, WidgetState } from '../types';
9
9
  * automatically as an empty `Set`).
10
10
  * @returns A reducer function compatible with `FlowSubject`.
11
11
  */
12
- export declare const createReducer: (value: WidgetStateInitial) => import("@equinor/fusion-observable").ReducerWithInitialState<WidgetState, import("@equinor/fusion-observable").PayloadAction<import("..").WidgetManifest, "set_manifest", {
13
- created: number;
14
- update: boolean | undefined;
15
- }, never> | import("@equinor/fusion-observable").PayloadAction<{
16
- key: string;
17
- args?: import("..").GetWidgetParameters["args"];
18
- }, "fetch_manifest::request", {
19
- update: boolean | undefined;
20
- }, never> | {
21
- payload: import("..").WidgetConfig;
22
- type: "set_config";
23
- } | import("@equinor/fusion-observable").PayloadAction<{
24
- key: string;
25
- args?: import("..").GetWidgetParameters["args"];
26
- }, "fetch_config::request", {
27
- update: boolean | undefined;
28
- }, never> | {
12
+ export declare const createReducer: (value: WidgetStateInitial) => import("@equinor/fusion-observable").ReducerWithInitialState<WidgetState, {
29
13
  payload: any;
30
14
  type: "set_module";
31
15
  } | {
32
- payload: string;
33
- type: "import_widget::request";
16
+ payload: unknown;
17
+ type: "fetch_config::failure";
34
18
  } | {
35
- payload: import("..").WidgetModulesInstance;
36
- type: "set_instance";
19
+ payload: unknown;
20
+ type: "fetch_manifest::failure";
21
+ } | {
22
+ payload: unknown;
23
+ type: "import_widget::failure";
24
+ } | {
25
+ payload: unknown;
26
+ type: "initialize_widget::failure";
37
27
  } | {
38
28
  payload: null;
39
29
  type: "initialize_widget::request";
40
30
  } | {
41
- payload: import("..").WidgetManifest;
42
- type: "fetch_manifest::success";
31
+ payload: null;
32
+ type: "initialize_widget::success";
43
33
  } | {
44
- payload: unknown;
45
- type: "fetch_manifest::failure";
34
+ payload: string;
35
+ type: "import_widget::request";
46
36
  } | {
47
37
  payload: import("..").WidgetConfig;
48
38
  type: "fetch_config::success";
49
39
  } | {
50
- payload: unknown;
51
- type: "fetch_config::failure";
40
+ payload: import("..").WidgetConfig;
41
+ type: "set_config";
42
+ } | {
43
+ payload: import("..").WidgetManifest;
44
+ type: "fetch_manifest::success";
52
45
  } | {
53
46
  payload: import("..").WidgetScriptModule;
54
47
  type: "import_widget::success";
55
48
  } | {
56
- payload: unknown;
57
- type: "import_widget::failure";
58
- } | {
59
- payload: null;
60
- type: "initialize_widget::success";
61
- } | {
62
- payload: unknown;
63
- type: "initialize_widget::failure";
64
- }>;
49
+ payload: import("..").WidgetModulesInstance;
50
+ type: "set_instance";
51
+ } | import("@equinor/fusion-observable").PayloadAction<import("..").WidgetManifest, "set_manifest", {
52
+ created: number;
53
+ update: boolean | undefined;
54
+ }, never> | import("@equinor/fusion-observable").PayloadAction<{
55
+ key: string;
56
+ args?: import("..").GetWidgetParameters['args'];
57
+ }, "fetch_manifest::request", {
58
+ update: boolean | undefined;
59
+ }, never> | import("@equinor/fusion-observable").PayloadAction<{
60
+ key: string;
61
+ args?: import("..").GetWidgetParameters['args'];
62
+ }, "fetch_config::request", {
63
+ update: boolean | undefined;
64
+ }, never>>;
65
65
  export default createReducer;
@@ -1 +1 @@
1
- export declare const version = "16.0.2";
1
+ export declare const version = "16.0.4";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@equinor/fusion-framework-module-widget",
3
- "version": "16.0.2",
3
+ "version": "16.0.4",
4
4
  "description": "",
5
5
  "main": "dist/esm/index.js",
6
6
  "types": "dist/types/index.d.ts",
@@ -38,22 +38,22 @@
38
38
  "dependencies": {
39
39
  "immer": "^11.0.0",
40
40
  "rxjs": "^7.8.1",
41
- "@equinor/fusion-observable": "9.1.0",
42
- "@equinor/fusion-query": "7.0.1"
41
+ "@equinor/fusion-observable": "9.1.1",
42
+ "@equinor/fusion-query": "7.0.3"
43
43
  },
44
44
  "devDependencies": {
45
- "typescript": "^6.0.3",
46
- "@equinor/fusion-framework-module": "^6.1.0",
47
- "@equinor/fusion-framework-module-http": "^8.0.3",
48
- "@equinor/fusion-framework-module-service-discovery": "^10.0.1",
49
- "@equinor/fusion-framework-module-event": "^6.0.0",
50
- "@equinor/fusion-query": "^7.0.1"
45
+ "typescript": "^7.0.2",
46
+ "@equinor/fusion-framework-module-http": "^8.0.5",
47
+ "@equinor/fusion-framework-module-event": "^6.0.1",
48
+ "@equinor/fusion-framework-module": "^6.1.1",
49
+ "@equinor/fusion-framework-module-service-discovery": "^10.0.2",
50
+ "@equinor/fusion-query": "^7.0.3"
51
51
  },
52
52
  "peerDependencies": {
53
- "@equinor/fusion-framework-module": "6.1.0",
54
- "@equinor/fusion-framework-module-event": "6.0.0",
55
- "@equinor/fusion-framework-module-http": "8.0.3",
56
- "@equinor/fusion-framework-module-service-discovery": "10.0.1"
53
+ "@equinor/fusion-framework-module": "6.1.1",
54
+ "@equinor/fusion-framework-module-http": "8.0.5",
55
+ "@equinor/fusion-framework-module-event": "6.0.1",
56
+ "@equinor/fusion-framework-module-service-discovery": "10.0.2"
57
57
  },
58
58
  "peerDependenciesMeta": {
59
59
  "@equinor/fusion-framework-module-event": {
package/src/Widget.ts CHANGED
@@ -51,7 +51,11 @@ export class Widget {
51
51
 
52
52
  #subscription = new Subscription();
53
53
 
54
- /** Current snapshot of the widget's internal state (manifest, config, modules, status). */
54
+ /**
55
+ * Current snapshot of the widget's internal state (manifest, config, modules, status).
56
+ *
57
+ * @returns The current {@link WidgetState}.
58
+ */
55
59
  get state(): WidgetState {
56
60
  return this.#state.value;
57
61
  }
@@ -165,8 +169,10 @@ export class Widget {
165
169
  */
166
170
  public getManifest(force_refresh = false): Observable<WidgetManifest> {
167
171
  return new Observable((subscriber) => {
172
+ // Emit the cached manifest immediately if one is already available
168
173
  if (this.#state.value.manifest) {
169
174
  subscriber.next(this.#state.value.manifest);
175
+ // Skip the fetch below unless the caller explicitly asked to refresh
170
176
  if (!force_refresh) {
171
177
  return subscriber.complete();
172
178
  }
@@ -209,8 +215,10 @@ export class Widget {
209
215
  public getConfig(force_refresh = false): Observable<WidgetConfig> {
210
216
  return new Observable((subscriber) => {
211
217
  const currentValue = this.#state.value;
218
+ // Emit the cached config immediately if the manifest and config are already available
212
219
  if (currentValue.manifest && currentValue.config) {
213
220
  subscriber.next(currentValue.config);
221
+ // Skip the fetch below unless the caller explicitly asked to refresh
214
222
  if (!force_refresh) {
215
223
  return subscriber.complete();
216
224
  }
@@ -273,8 +281,10 @@ export class Widget {
273
281
  */
274
282
  public getWidgetModule(force_refresh = false): Observable<WidgetScriptModule> {
275
283
  return new Observable((subscriber) => {
284
+ // Emit the cached script module immediately if one is already available
276
285
  if (this.#state.value.modules) {
277
286
  subscriber.next(this.#state.value.modules);
287
+ // Skip the import below unless the caller explicitly asked to refresh
278
288
  if (!force_refresh) {
279
289
  return subscriber.complete();
280
290
  }
@@ -345,7 +355,8 @@ export class Widget {
345
355
  },
346
356
  }),
347
357
  error: (err) => {
348
- observer.error(err), this.#state.next(actions.initialize.failure(err));
358
+ observer.error(err);
359
+ this.#state.next(actions.initialize.failure(err));
349
360
  },
350
361
  complete: () => {
351
362
  this.#state.next(actions.initialize.success());
@@ -0,0 +1,61 @@
1
+ /**
2
+ * Discriminator for categorizing widget HTTP errors.
3
+ *
4
+ * - `'not_found'` — HTTP 404
5
+ * - `'unauthorized'` — HTTP 401
6
+ * - `'unknown'` — any other error
7
+ */
8
+ export type WidgetErrorType = 'not_found' | 'unauthorized' | 'unknown';
9
+
10
+ /**
11
+ * Error thrown when a widget manifest cannot be loaded from the backend API.
12
+ *
13
+ * Use the static {@link fromHttpResponse} factory to create instances from
14
+ * HTTP responses with appropriate type mapping.
15
+ */
16
+ export class WidgetManifestLoadError extends Error {
17
+ /**
18
+ * Creates a `WidgetManifestLoadError` from an HTTP `Response`.
19
+ *
20
+ * Maps HTTP 401 to `'unauthorized'`, 404 to `'not_found'`, and all other
21
+ * status codes to `'unknown'`.
22
+ *
23
+ * @param response - The failing HTTP response.
24
+ * @param options - Standard `ErrorOptions` (e.g., `cause`).
25
+ * @returns A typed `WidgetManifestLoadError`.
26
+ */
27
+ static fromHttpResponse(response: Response, options?: ErrorOptions) {
28
+ // Map known status codes to a specific error type, otherwise fall through to 'unknown'
29
+ switch (response.status) {
30
+ case 401:
31
+ return new WidgetManifestLoadError(
32
+ 'unauthorized',
33
+ 'failed to load widget manifest, request not authorized',
34
+ options,
35
+ );
36
+ case 404:
37
+ return new WidgetManifestLoadError('not_found', 'widget manifest not found', options);
38
+ }
39
+ return new WidgetManifestLoadError(
40
+ 'unknown',
41
+ `failed to load widget manifest, status code ${response.status}`,
42
+ options,
43
+ );
44
+ }
45
+ /**
46
+ * @param type - Error category discriminator.
47
+ * @param message - Human-readable error description.
48
+ * @param options - Standard `ErrorOptions` (e.g., `cause`).
49
+ */
50
+ constructor(
51
+ public readonly type: WidgetErrorType,
52
+ message?: string,
53
+ options?: ErrorOptions,
54
+ ) {
55
+ super(message, options);
56
+ this.name = 'GetWidgetLoadManifestErrors';
57
+ }
58
+ }
59
+
60
+ export { WidgetConfigLoadError } from './errors/WidgetConfigLoadError.js';
61
+ export { WidgetScriptModuleError } from './errors/WidgetScriptModuleError.js';
@@ -63,6 +63,7 @@ export class WidgetModuleConfigurator extends BaseConfigBuilder<WidgetModuleConf
63
63
  private async _createHttpClient(clientId: string, init: ConfigBuilderCallbackArgs) {
64
64
  const http = await init.requireInstance('http');
65
65
 
66
+ // Reuse an already-registered client when one exists for this id
66
67
  if (http.hasClient(clientId)) {
67
68
  return http.createClient(clientId);
68
69
  } else {
@@ -86,6 +87,7 @@ export class WidgetModuleConfigurator extends BaseConfigBuilder<WidgetModuleConf
86
87
  ) {
87
88
  const httpClient = await this._createHttpClient('apps', _init);
88
89
 
90
+ // Only fall back to the default client when the caller hasn't set one
89
91
  if (!config.client) {
90
92
  config.client = createDefaultClient(httpClient);
91
93
  }
@@ -9,7 +9,7 @@ import { Query } from '@equinor/fusion-query';
9
9
  import type { GetWidgetParameters, WidgetConfig, WidgetManifest } from './types';
10
10
 
11
11
  import type { WidgetModuleConfig } from './WidgetModuleConfigurator';
12
- import { WidgetManifestLoadError, WidgetConfigLoadError } from './errors';
12
+ import { WidgetManifestLoadError, WidgetConfigLoadError } from './WidgetManifestLoadError';
13
13
  import { Widget } from './Widget';
14
14
 
15
15
  /**
@@ -143,6 +143,7 @@ export class WidgetModuleProvider implements IWidgetModuleProvider {
143
143
  * @param widgetKey - Widget identifier.
144
144
  * @param args - Optional version or tag selector.
145
145
  * @returns Observable emitting the {@link WidgetConfig}.
146
+ * @throws {WidgetManifestLoadError} When the underlying query fails.
146
147
  */
147
148
  protected _getWidgetConfig(
148
149
  widgetKey: GetWidgetParameters['widgetKey'],
@@ -150,7 +151,9 @@ export class WidgetModuleProvider implements IWidgetModuleProvider {
150
151
  ): Observable<WidgetConfig> {
151
152
  const client = new Query(this.#config.client.getWidgetConfig);
152
153
  this.#subscription.add(() => client.complete());
154
+ // Execute the query and handle errors
153
155
  return Query.extractQueryValue(
156
+ // Map any query failure to a typed `WidgetManifestLoadError`
154
157
  client.query({ widgetKey, args }).pipe(
155
158
  catchError((err) => {
156
159
  // Extract the cause since the error will be a `QueryError`
@@ -160,6 +163,7 @@ export class WidgetModuleProvider implements IWidgetModuleProvider {
160
163
  if (cause instanceof WidgetManifestLoadError) {
161
164
  throw cause;
162
165
  }
166
+ // Map HTTP failures to a `WidgetManifestLoadError` carrying the response
163
167
  if (cause instanceof HttpResponseError) {
164
168
  throw WidgetManifestLoadError.fromHttpResponse(cause.response, {
165
169
  cause,
@@ -181,6 +185,7 @@ export class WidgetModuleProvider implements IWidgetModuleProvider {
181
185
  * @param widgetKey - Widget identifier.
182
186
  * @param args - Optional version or tag selector.
183
187
  * @returns Observable emitting the {@link WidgetManifest}.
188
+ * @throws {WidgetConfigLoadError} When the underlying query fails.
184
189
  */
185
190
  protected _getWidget(
186
191
  widgetKey: GetWidgetParameters['widgetKey'],
@@ -192,6 +197,7 @@ export class WidgetModuleProvider implements IWidgetModuleProvider {
192
197
 
193
198
  // Execute the query and handle errors
194
199
  return Query.extractQueryValue(
200
+ // Map any query failure to a typed `WidgetConfigLoadError`
195
201
  client.query({ widgetKey, args }).pipe(
196
202
  catchError((err) => {
197
203
  // Extract the cause since the error will be a `QueryError`
@@ -201,6 +207,7 @@ export class WidgetModuleProvider implements IWidgetModuleProvider {
201
207
  if (cause instanceof WidgetConfigLoadError) {
202
208
  throw cause;
203
209
  }
210
+ // Map HTTP failures to a `WidgetConfigLoadError` carrying the response
204
211
  if (cause instanceof HttpResponseError) {
205
212
  throw WidgetConfigLoadError.fromHttpResponse(cause.response, { cause });
206
213
  }
@@ -22,7 +22,7 @@ import type { WidgetModuleConfigBuilderCallback } from './WidgetModuleConfigurat
22
22
  * ```
23
23
  */
24
24
  export const enableWidgetModule = (
25
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
25
+ // biome-ignore lint/suspicious/noExplicitAny: `IModulesConfigurator<any, any>` widens to accept a configurator for any concrete module set — `unknown` would break assignability of a real configurator instance to this parameter
26
26
  configurator: IModulesConfigurator<any, any>,
27
27
  builder?: WidgetModuleConfigBuilderCallback,
28
28
  ): void => {
@@ -0,0 +1,51 @@
1
+ import type { WidgetErrorType } from '../WidgetManifestLoadError.js';
2
+
3
+ /**
4
+ * Error thrown when a widget configuration cannot be loaded from the backend API.
5
+ *
6
+ * Use the static {@link fromHttpResponse} factory to create instances from
7
+ * HTTP responses with appropriate type mapping.
8
+ */
9
+ export class WidgetConfigLoadError extends Error {
10
+ /**
11
+ * Creates a `WidgetConfigLoadError` from an HTTP `Response`.
12
+ *
13
+ * Maps HTTP 401 to `'unauthorized'`, 404 to `'not_found'`, and all other
14
+ * status codes to `'unknown'`.
15
+ *
16
+ * @param response - The failing HTTP response.
17
+ * @param options - Standard `ErrorOptions` (e.g., `cause`).
18
+ * @returns A typed `WidgetConfigLoadError`.
19
+ */
20
+ static fromHttpResponse(response: Response, options?: ErrorOptions) {
21
+ // Map known status codes to a specific error type, otherwise fall through to 'unknown'
22
+ switch (response.status) {
23
+ case 401:
24
+ return new WidgetConfigLoadError(
25
+ 'unauthorized',
26
+ 'failed to load widget config, request not authorized',
27
+ options,
28
+ );
29
+ case 404:
30
+ return new WidgetConfigLoadError('not_found', 'widget config not found', options);
31
+ }
32
+ return new WidgetConfigLoadError(
33
+ 'unknown',
34
+ `failed to load widget config, status code ${response.status}`,
35
+ options,
36
+ );
37
+ }
38
+ /**
39
+ * @param type - Error category discriminator.
40
+ * @param message - Human-readable error description.
41
+ * @param options - Standard `ErrorOptions` (e.g., `cause`).
42
+ */
43
+ constructor(
44
+ public readonly type: WidgetErrorType,
45
+ message?: string,
46
+ options?: ErrorOptions,
47
+ ) {
48
+ super(message, options);
49
+ this.name = 'GetWidgetLoadConfigError';
50
+ }
51
+ }
@@ -0,0 +1,20 @@
1
+ import type { WidgetErrorType } from '../WidgetManifestLoadError.js';
2
+
3
+ /**
4
+ * Error thrown when a widget script module cannot be dynamically imported.
5
+ */
6
+ export class WidgetScriptModuleError extends Error {
7
+ /**
8
+ * @param type - Error category discriminator.
9
+ * @param message - Human-readable error description.
10
+ * @param options - Standard `ErrorOptions` (e.g., `cause`).
11
+ */
12
+ constructor(
13
+ public readonly type: WidgetErrorType,
14
+ message?: string,
15
+ options?: ErrorOptions,
16
+ ) {
17
+ super(message, options);
18
+ this.name = 'WidgetScriptModuleError';
19
+ }
20
+ }
package/src/module.ts CHANGED
@@ -42,6 +42,8 @@ export const module: WidgetModule = {
42
42
  return new WidgetModuleProvider({ config, event });
43
43
  },
44
44
  dispose: (args) => {
45
+ // `args.instance` is typed as the generic module instance, but the module descriptor
46
+ // guarantees it was created as a `WidgetModuleProvider` — safe to cast for disposal.
45
47
  (args.instance as unknown as WidgetModuleProvider).dispose();
46
48
  },
47
49
  };
@@ -53,7 +53,7 @@ const createActions = () => ({
53
53
  (error: unknown) => ({ payload: error }),
54
54
  ),
55
55
  /** Sets the dynamically imported widget script module in state. */
56
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
56
+ // biome-ignore lint/suspicious/noExplicitAny: module payload widens to accept any script module shape when set
57
57
  setModule: createAction('set_module', (module: any) => ({ payload: module })),
58
58
  /** Async action triplet for dynamically importing the widget script. */
59
59
  importWidget: createAsyncAction(
@@ -23,10 +23,13 @@ import type { WidgetStateInitial, WidgetState } from '../types';
23
23
  * @returns A reducer function compatible with `FlowSubject`.
24
24
  */
25
25
  export const createReducer = (value: WidgetStateInitial) =>
26
+ // Seed the reducer's initial state with an empty in-progress status set.
26
27
  makeReducer<WidgetState, Actions>({ ...value, status: new Set() } as WidgetState, (builder) =>
27
28
  builder
28
29
  .addCase(actions.setManifest, (state, action) => {
30
+ // Merge with existing state when caller requested an update, otherwise replace
29
31
  if (action.meta.update) {
32
+ // Shallow-merge the new payload over the existing manifest fields.
30
33
  state.manifest = { ...state.manifest, ...action.payload };
31
34
  } else {
32
35
  state.manifest = action.payload;