@equinor/fusion-framework-module-app 8.0.2 → 8.0.3

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 (109) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/dist/esm/AppClient.js +75 -8
  3. package/dist/esm/AppClient.js.map +1 -1
  4. package/dist/esm/AppConfig.js +9 -1
  5. package/dist/esm/AppConfig.js.map +1 -1
  6. package/dist/esm/{AppClient.Selectors.js → AppConfigSelector.js} +1 -1
  7. package/dist/esm/AppConfigSelector.js.map +1 -0
  8. package/dist/esm/AppConfigurator.js +28 -2
  9. package/dist/esm/AppConfigurator.js.map +1 -1
  10. package/dist/esm/AppModuleProvider.js +16 -0
  11. package/dist/esm/AppModuleProvider.js.map +1 -1
  12. package/dist/esm/app/App.js +82 -24
  13. package/dist/esm/app/App.js.map +1 -1
  14. package/dist/esm/app/actions.js +2 -2
  15. package/dist/esm/app/actions.js.map +1 -1
  16. package/dist/esm/app/create-reducer.js +2 -1
  17. package/dist/esm/app/create-reducer.js.map +1 -1
  18. package/dist/esm/app/filter-empty.js +11 -0
  19. package/dist/esm/app/filter-empty.js.map +1 -0
  20. package/dist/esm/app/flows/handle-fetch-config.js +30 -0
  21. package/dist/esm/app/flows/handle-fetch-config.js.map +1 -0
  22. package/dist/esm/app/flows/handle-fetch-manifest.js +30 -0
  23. package/dist/esm/app/flows/handle-fetch-manifest.js.map +1 -0
  24. package/dist/esm/app/flows/handle-fetch-settings.js +30 -0
  25. package/dist/esm/app/flows/handle-fetch-settings.js.map +1 -0
  26. package/dist/esm/app/flows/handle-import-application.js +23 -0
  27. package/dist/esm/app/flows/handle-import-application.js.map +1 -0
  28. package/dist/esm/app/flows/handle-update-settings.js +22 -0
  29. package/dist/esm/app/flows/handle-update-settings.js.map +1 -0
  30. package/dist/esm/app/flows/index.js +6 -0
  31. package/dist/esm/app/flows/index.js.map +1 -0
  32. package/dist/esm/enable-app-module.js +2 -1
  33. package/dist/esm/enable-app-module.js.map +1 -1
  34. package/dist/esm/errors/AppBuildError.js +35 -0
  35. package/dist/esm/errors/AppBuildError.js.map +1 -0
  36. package/dist/esm/errors/AppConfigError.js +35 -0
  37. package/dist/esm/errors/AppConfigError.js.map +1 -0
  38. package/dist/esm/errors/AppManifestError.js +35 -0
  39. package/dist/esm/errors/AppManifestError.js.map +1 -0
  40. package/dist/esm/errors/AppScriptModuleError.js +17 -0
  41. package/dist/esm/errors/AppScriptModuleError.js.map +1 -0
  42. package/dist/esm/errors/AppSettingsError.js +35 -0
  43. package/dist/esm/errors/AppSettingsError.js.map +1 -0
  44. package/dist/esm/errors/app-error-type.js +2 -0
  45. package/dist/esm/errors/app-error-type.js.map +1 -0
  46. package/dist/esm/errors.js +5 -148
  47. package/dist/esm/errors.js.map +1 -1
  48. package/dist/esm/module.js +2 -0
  49. package/dist/esm/module.js.map +1 -1
  50. package/dist/esm/schemas.js +4 -0
  51. package/dist/esm/schemas.js.map +1 -1
  52. package/dist/esm/version.js +1 -1
  53. package/dist/tsconfig.tsbuildinfo +1 -1
  54. package/dist/types/AppClient.d.ts +42 -0
  55. package/dist/types/AppConfig.d.ts +7 -1
  56. package/dist/types/AppConfigurator.d.ts +25 -0
  57. package/dist/types/AppModuleProvider.d.ts +10 -0
  58. package/dist/types/app/App.d.ts +49 -8
  59. package/dist/types/app/create-reducer.d.ts +55 -55
  60. package/dist/types/app/filter-empty.d.ts +8 -0
  61. package/dist/types/app/flows/handle-fetch-config.d.ts +12 -0
  62. package/dist/types/app/flows/handle-fetch-manifest.d.ts +12 -0
  63. package/dist/types/app/flows/handle-fetch-settings.d.ts +12 -0
  64. package/dist/types/app/flows/handle-import-application.d.ts +10 -0
  65. package/dist/types/app/flows/handle-update-settings.d.ts +12 -0
  66. package/dist/types/app/flows/index.d.ts +5 -0
  67. package/dist/types/errors/AppBuildError.d.ts +21 -0
  68. package/dist/types/errors/AppConfigError.d.ts +21 -0
  69. package/dist/types/errors/AppManifestError.d.ts +21 -0
  70. package/dist/types/errors/AppScriptModuleError.d.ts +14 -0
  71. package/dist/types/errors/AppSettingsError.d.ts +21 -0
  72. package/dist/types/errors/app-error-type.d.ts +9 -0
  73. package/dist/types/errors.d.ts +6 -103
  74. package/dist/types/version.d.ts +1 -1
  75. package/package.json +9 -9
  76. package/src/AppClient.ts +65 -4
  77. package/src/AppConfig.ts +9 -1
  78. package/src/AppConfigurator.ts +28 -4
  79. package/src/AppModuleProvider.ts +16 -0
  80. package/src/app/App.ts +88 -31
  81. package/src/app/actions.ts +2 -2
  82. package/src/app/create-reducer.ts +2 -1
  83. package/src/app/filter-empty.ts +11 -0
  84. package/src/app/flows/handle-fetch-config.ts +48 -0
  85. package/src/app/flows/handle-fetch-manifest.ts +53 -0
  86. package/src/app/flows/handle-fetch-settings.ts +50 -0
  87. package/src/app/flows/handle-import-application.ts +37 -0
  88. package/src/app/flows/handle-update-settings.ts +39 -0
  89. package/src/app/flows/index.ts +5 -0
  90. package/src/app/types.ts +2 -2
  91. package/src/enable-app-module.ts +2 -1
  92. package/src/errors/AppBuildError.ts +47 -0
  93. package/src/errors/AppConfigError.ts +47 -0
  94. package/src/errors/AppManifestError.ts +47 -0
  95. package/src/errors/AppScriptModuleError.ts +20 -0
  96. package/src/errors/AppSettingsError.ts +47 -0
  97. package/src/errors/app-error-type.ts +9 -0
  98. package/src/errors.ts +6 -208
  99. package/src/module.ts +2 -0
  100. package/src/schemas.ts +4 -0
  101. package/src/types.ts +1 -1
  102. package/src/version.ts +1 -1
  103. package/dist/esm/AppClient.Selectors.js.map +0 -1
  104. package/dist/esm/app/flows.js +0 -120
  105. package/dist/esm/app/flows.js.map +0 -1
  106. package/dist/types/app/flows.d.ts +0 -41
  107. package/src/app/flows.ts +0 -189
  108. /package/dist/types/{AppClient.Selectors.d.ts → AppConfigSelector.d.ts} +0 -0
  109. /package/src/{AppClient.Selectors.ts → AppConfigSelector.ts} +0 -0
@@ -1,103 +1,6 @@
1
- /**
2
- * Discriminant for application-related errors.
3
- *
4
- * - `'not_found'` – The requested resource does not exist (HTTP 404).
5
- * - `'unauthorized'` – The request lacks valid credentials (HTTP 401).
6
- * - `'deleted'` The resource has been removed (HTTP 410).
7
- * - `'unknown'` – An unexpected failure occurred.
8
- */
9
- type AppErrorType = 'not_found' | 'unauthorized' | 'unknown' | 'deleted';
10
- /**
11
- * Represents an error that occurs when loading an application manifest.
12
- */
13
- export declare class AppManifestError extends Error {
14
- readonly type: AppErrorType;
15
- /**
16
- * Creates an instance of AppManifestError based on the HTTP response status.
17
- * @param response The HTTP response.
18
- * @param options Optional error options.
19
- * @returns An instance of AppManifestError.
20
- */
21
- static fromHttpResponse(response: Response, options?: ErrorOptions): AppManifestError;
22
- /**
23
- * Creates an instance of AppManifestError.
24
- * @param type The type of the error.
25
- * @param message The error message.
26
- * @param options Optional error options.
27
- */
28
- constructor(type: AppErrorType, message?: string, options?: ErrorOptions);
29
- }
30
- /**
31
- * Represents an error that occurs in the application configuration.
32
- */
33
- export declare class AppConfigError extends Error {
34
- readonly type: AppErrorType;
35
- /**
36
- * Creates an instance of `AppConfigError` based on the HTTP response status.
37
- * @param response The HTTP response.
38
- * @param options Additional error options.
39
- * @returns An instance of `AppConfigError` based on the HTTP response status.
40
- */
41
- static fromHttpResponse(response: Response, options?: ErrorOptions): AppConfigError;
42
- /**
43
- * Creates an instance of `AppConfigError`.
44
- * @param type The type of the application error.
45
- * @param message The error message.
46
- * @param options Additional error options.
47
- */
48
- constructor(type: AppErrorType, message?: string, options?: ErrorOptions);
49
- }
50
- /**
51
- * Represents an error that occurs in the application build.
52
- */
53
- export declare class AppBuildError extends Error {
54
- readonly type: AppErrorType;
55
- /**
56
- * Creates an instance of `AppBuildError` based on the HTTP response status.
57
- * @param response The HTTP response.
58
- * @param options Additional error options.
59
- * @returns An instance of `AppBuildError` based on the HTTP response status.
60
- */
61
- static fromHttpResponse(response: Response, options?: ErrorOptions): AppBuildError;
62
- /**
63
- * Creates an instance of `AppBuildError`.
64
- * @param type The type of the application error.
65
- * @param message The error message.
66
- * @param options Additional error options.
67
- */
68
- constructor(type: AppErrorType, message?: string, options?: ErrorOptions);
69
- }
70
- /**
71
- * Represents an error that occurs while fetching application settings.
72
- */
73
- export declare class AppSettingsError extends Error {
74
- readonly type: AppErrorType;
75
- /**
76
- * Creates an instance of `AppSettingsError` based on the HTTP response status.
77
- * @param response The HTTP response.
78
- * @param options Additional error options.
79
- * @returns An instance of `AppSettingsError` based on the HTTP response status.
80
- */
81
- static fromHttpResponse(response: Response, options?: ErrorOptions): AppSettingsError;
82
- /**
83
- * Creates an instance of `AppSettingsError`.
84
- * @param type The type of the application error.
85
- * @param message The error message.
86
- * @param options Additional error options.
87
- */
88
- constructor(type: AppErrorType, message?: string, options?: ErrorOptions);
89
- }
90
- /**
91
- * Represents an error that occurs when loading the application script.
92
- */
93
- export declare class AppScriptModuleError extends Error {
94
- readonly type: AppErrorType;
95
- /**
96
- * Creates a new instance of the AppScriptModuleError class.
97
- * @param type The type of the error.
98
- * @param message The error message.
99
- * @param options Additional options for the error.
100
- */
101
- constructor(type: AppErrorType, message?: string, options?: ErrorOptions);
102
- }
103
- export {};
1
+ export { AppManifestError } from './errors/AppManifestError';
2
+ export { AppConfigError } from './errors/AppConfigError';
3
+ export { AppBuildError } from './errors/AppBuildError';
4
+ export { AppSettingsError } from './errors/AppSettingsError';
5
+ export { AppScriptModuleError } from './errors/AppScriptModuleError';
6
+ export type { AppErrorType } from './errors/app-error-type';
@@ -1 +1 @@
1
- export declare const version = "8.0.2";
1
+ export declare const version = "8.0.3";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@equinor/fusion-framework-module-app",
3
- "version": "8.0.2",
3
+ "version": "8.0.3",
4
4
  "description": "",
5
5
  "main": "dist/esm/index.js",
6
6
  "exports": {
@@ -61,16 +61,16 @@
61
61
  "rxjs": "^7.8.1",
62
62
  "uuid": "^14.0.0",
63
63
  "zod": "^4.4.3",
64
- "@equinor/fusion-query": "^7.0.1",
65
- "@equinor/fusion-observable": "^9.0.1"
64
+ "@equinor/fusion-observable": "^9.1.1",
65
+ "@equinor/fusion-query": "^7.0.2"
66
66
  },
67
67
  "devDependencies": {
68
- "typescript": "^6.0.3",
69
- "@equinor/fusion-framework-module": "^6.0.0",
70
- "@equinor/fusion-framework-module-http": "^8.0.1",
71
- "@equinor/fusion-framework-module-service-discovery": "^10.0.1",
72
- "@equinor/fusion-framework-module-msal": "^8.0.4",
73
- "@equinor/fusion-framework-module-event": "^6.0.0"
68
+ "typescript": "^7.0.2",
69
+ "@equinor/fusion-framework-module": "^6.1.1",
70
+ "@equinor/fusion-framework-module-event": "^6.0.1",
71
+ "@equinor/fusion-framework-module-http": "^8.0.4",
72
+ "@equinor/fusion-framework-module-msal": "^10.0.2",
73
+ "@equinor/fusion-framework-module-service-discovery": "^10.0.2"
74
74
  },
75
75
  "scripts": {
76
76
  "build": "tsc -b"
package/src/AppClient.ts CHANGED
@@ -20,7 +20,7 @@ import type {
20
20
  ConfigEnvironment,
21
21
  } from './types';
22
22
  import { AppBuildError, AppConfigError, AppManifestError, AppSettingsError } from './errors';
23
- import { AppConfigSelector } from './AppClient.Selectors';
23
+ import { AppConfigSelector } from './AppConfigSelector';
24
24
 
25
25
  /**
26
26
  * Contract for an app service client that fetches application manifests,
@@ -102,11 +102,11 @@ const ApplicationSchema = ApiApplicationSchema.transform((x): AppManifest => {
102
102
  const { category, ...props } = x;
103
103
  return {
104
104
  ...props,
105
- // TODO: remove deprecated appKey
105
+ // TODO(#5124): remove deprecated appKey
106
106
  get key() {
107
107
  return props.appKey;
108
108
  },
109
- // TODO: remove deprecated name
109
+ // TODO(#5124): remove deprecated name
110
110
  get name() {
111
111
  return props.displayName;
112
112
  },
@@ -139,6 +139,10 @@ export class AppClient implements IAppClient {
139
139
  #settings: Query<AppSettings, { appKey: string; settings?: AppSettings }>;
140
140
  #client: IHttpClient;
141
141
 
142
+ /**
143
+ * Creates a new {@link AppClient} backed by the given HTTP client.
144
+ * @param client - The HTTP client used to communicate with the Fusion app service API.
145
+ */
142
146
  constructor(client: IHttpClient) {
143
147
  this.#client = client;
144
148
 
@@ -229,15 +233,26 @@ export class AppClient implements IAppClient {
229
233
  });
230
234
  }
231
235
 
236
+ /**
237
+ * Fetches the build metadata for an application.
238
+ * @param args - Object containing the `appKey` and an optional version `tag`.
239
+ * @returns An observable that emits the resolved {@link AppBuildManifest}.
240
+ * @throws {AppBuildError} When the build metadata cannot be loaded.
241
+ */
232
242
  getAppBuild(args: { appKey: string; tag?: string }): Observable<AppBuildManifest> {
243
+ // unwrap the build manifest and map errors to a typed AppBuildError
233
244
  return this.#build.query(args).pipe(
245
+ // unwrap the query result into the build manifest
234
246
  map((res) => res.value as AppBuildManifest),
247
+ // map http/unknown errors into a typed AppBuildError
235
248
  catchError((err) => {
236
249
  const cause = err?.cause || err;
237
250
 
251
+ // rethrow already-typed errors as-is
238
252
  if (cause instanceof AppBuildError) {
239
253
  throw cause;
240
254
  }
255
+ // map http errors to a typed AppBuildError
241
256
  if (cause instanceof HttpJsonResponseError || cause instanceof HttpResponseError) {
242
257
  throw AppBuildError.fromHttpResponse(cause.response, { cause });
243
258
  }
@@ -246,16 +261,26 @@ export class AppClient implements IAppClient {
246
261
  );
247
262
  }
248
263
 
264
+ /**
265
+ * Fetches the manifest for a single application.
266
+ * @param args - Object containing the `appKey` and an optional version `tag`.
267
+ * @returns An observable that emits the resolved {@link AppManifest}.
268
+ * @throws {AppManifestError} When the manifest cannot be loaded.
269
+ */
249
270
  getAppManifest(args: { appKey: string; tag?: string }): Observable<AppManifest> {
271
+ // unwrap the manifest and map errors to a typed AppManifestError
250
272
  return this.#manifest.query(args).pipe(
251
273
  queryValue,
274
+ // map http/unknown errors into a typed AppManifestError
252
275
  catchError((err) => {
253
276
  const cause = err?.cause || err;
254
277
 
278
+ // rethrow already-typed errors as-is
255
279
  if (cause instanceof AppManifestError) {
256
280
  throw cause;
257
281
  }
258
282
 
283
+ // map http errors to a typed AppManifestError
259
284
  if (cause instanceof HttpJsonResponseError || cause instanceof HttpResponseError) {
260
285
  throw AppManifestError.fromHttpResponse(cause.response, { cause });
261
286
  }
@@ -265,23 +290,41 @@ export class AppClient implements IAppClient {
265
290
  );
266
291
  }
267
292
 
293
+ /**
294
+ * Fetches manifests for all registered applications.
295
+ * @param args - Optional filter; set `filterByCurrentUser` to `true` to return only apps the authenticated user has access to.
296
+ * @returns An observable that emits an array of {@link AppManifest} objects.
297
+ */
268
298
  getAppManifests(args: { filterByCurrentUser?: boolean } | undefined): Observable<AppManifest[]> {
299
+ // unwrap the query result into the manifest array
269
300
  return this.#manifests.query(args).pipe(queryValue);
270
301
  }
271
302
 
303
+ /**
304
+ * Fetches the runtime configuration for an application.
305
+ * @template TType - Shape of the `environment` record in the returned config.
306
+ * @param args - Object containing the `appKey` and an optional version `tag`.
307
+ * @returns An observable that emits the resolved {@link AppConfig}.
308
+ * @throws {AppConfigError} When the configuration cannot be loaded.
309
+ */
272
310
  getAppConfig<TType extends ConfigEnvironment = ConfigEnvironment>(args: {
273
311
  appKey: string;
274
312
  tag?: string;
275
313
  }): Observable<AppConfig<TType>> {
314
+ // unwrap the config and map errors to a typed AppConfigError
276
315
  return this.#config.query(args).pipe(
316
+ // unwrap the query result into the config
277
317
  map((res) => res.value as AppConfig<TType>),
318
+ // map http/unknown errors into a typed AppConfigError
278
319
  catchError((err) => {
279
320
  /** handle both direct errors and errors wrapped in a `cause` property */
280
321
  const cause = err?.cause || err;
281
322
 
323
+ // rethrow already-typed errors as-is
282
324
  if (cause instanceof AppConfigError) {
283
325
  throw cause;
284
326
  }
327
+ // map http errors to a typed AppConfigError
285
328
  if (cause instanceof HttpJsonResponseError || cause instanceof HttpResponseError) {
286
329
  throw AppConfigError.fromHttpResponse(cause.response, { cause });
287
330
  }
@@ -290,16 +333,26 @@ export class AppClient implements IAppClient {
290
333
  );
291
334
  }
292
335
 
336
+ /**
337
+ * Fetches per-user settings for an application.
338
+ * @param args - Object containing the `appKey`.
339
+ * @returns An observable that emits the {@link AppSettings} record.
340
+ * @throws {AppSettingsError} When settings cannot be loaded.
341
+ */
293
342
  getAppSettings(args: { appKey: string }): Observable<AppSettings> {
343
+ // unwrap the settings and map errors to a typed AppSettingsError
294
344
  return this.#settings.query(args).pipe(
295
345
  queryValue,
346
+ // map http/unknown errors into a typed AppSettingsError
296
347
  catchError((err) => {
297
348
  /** handle both direct errors and errors wrapped in a `cause` property */
298
349
  const cause = err?.cause || err;
299
350
 
351
+ // rethrow already-typed errors as-is
300
352
  if (cause instanceof AppSettingsError) {
301
353
  throw cause;
302
354
  }
355
+ // map http errors to a typed AppSettingsError
303
356
  if (cause instanceof HttpJsonResponseError || cause instanceof HttpResponseError) {
304
357
  throw AppSettingsError.fromHttpResponse(cause.response, { cause });
305
358
  }
@@ -308,11 +361,15 @@ export class AppClient implements IAppClient {
308
361
  );
309
362
  }
310
363
 
364
+ /**
365
+ * Persists updated per-user settings for an application via PUT.
366
+ * @param args - Object containing the `appKey` and the `settings` payload to save.
367
+ * @returns An observable that emits the persisted {@link AppSettings}.
368
+ */
311
369
  updateAppSettings(args: { appKey: string; settings: AppSettings }): Observable<AppSettings> {
312
370
  const { appKey, settings } = args;
313
371
  return (
314
372
  this.#client
315
- // execute PUT request to update settings
316
373
  .json$<AppSettings>(`/persons/me/apps/${appKey}/settings`, {
317
374
  method: 'PUT',
318
375
  body: settings,
@@ -320,6 +377,7 @@ export class AppClient implements IAppClient {
320
377
  'Api-Version': '1.0',
321
378
  },
322
379
  })
380
+ // update the settings cache with the persisted value
323
381
  .pipe(
324
382
  tap((value) => {
325
383
  // update cache with new settings
@@ -335,6 +393,9 @@ export class AppClient implements IAppClient {
335
393
  );
336
394
  }
337
395
 
396
+ /**
397
+ * Disposes of the client, completing all internal query streams.
398
+ */
338
399
  [Symbol.dispose]() {
339
400
  console.warn('AppClient disposed');
340
401
  this.#manifest.complete();
package/src/AppConfig.ts CHANGED
@@ -6,7 +6,9 @@
6
6
  * @returns The same object, deeply frozen.
7
7
  */
8
8
  const deepFreeze = <T extends Record<string, unknown>>(obj: T): T => {
9
+ // recursively freeze any nested, unfrozen object properties
9
10
  for (const property of Object.keys(obj)) {
11
+ // only descend into unfrozen object properties
10
12
  if (
11
13
  typeof obj[property] === 'object' &&
12
14
  obj[property] !== null &&
@@ -59,18 +61,24 @@ export class AppConfig<TEnvironment extends ConfigEnvironment = ConfigEnvironmen
59
61
 
60
62
  /**
61
63
  * The environment configuration for the application.
64
+ * @returns The frozen environment configuration.
62
65
  */
63
66
  get environment(): TEnvironment {
64
67
  return this.#environment;
65
68
  }
66
69
 
67
70
  /**
68
- * The configuration endpoints for the application,.
71
+ * The configuration endpoints for the application.
72
+ * @returns The frozen map of configuration endpoints.
69
73
  */
70
74
  get endpoints(): Record<string, ConfigEndPoint> {
71
75
  return this.#endpoints;
72
76
  }
73
77
 
78
+ /**
79
+ * Creates a new {@link AppConfig}, deep-freezing the provided environment and endpoints.
80
+ * @param config - The environment and endpoints to initialize the configuration with.
81
+ */
74
82
  constructor(config: {
75
83
  environment?: TEnvironment | null;
76
84
  endpoints?: Record<string, ConfigEndPoint>;
@@ -6,8 +6,6 @@ import {
6
6
  import type { HttpModule, IHttpClient } from '@equinor/fusion-framework-module-http';
7
7
  import type { ServiceDiscoveryModule } from '@equinor/fusion-framework-module-service-discovery';
8
8
 
9
- import { moduleKey } from './module';
10
-
11
9
  import AppClient, { type IAppClient } from './AppClient';
12
10
 
13
11
  /**
@@ -64,7 +62,13 @@ export class AppConfigurator
64
62
  defaultExpireTime = 1 * 60 * 1000;
65
63
 
66
64
  /**
65
+ * Creates the default HTTP client for the app service, preferring a pre-configured
66
+ * client from the http module and falling back to service discovery.
67
+ *
67
68
  * WARNING: this function will be remove in future
69
+ *
70
+ * @param init - Module initializer args providing access to the http and service discovery modules.
71
+ * @returns A promise resolving to the {@link IHttpClient} used to communicate with the app service.
68
72
  */
69
73
  protected async _createHttpClient(
70
74
  init: ModuleInitializerArgs<IAppConfigurator, [HttpModule, ServiceDiscoveryModule]>,
@@ -79,11 +83,16 @@ export class AppConfigurator
79
83
  /** load service discovery module */
80
84
  const serviceDiscovery = await init.requireInstance('serviceDiscovery');
81
85
 
82
- // TODO - remove when refactor portal service!
86
+ // TODO(#5125) - remove when refactor portal service!
83
87
  /** resolve and create a client from discovery */
84
88
  return await serviceDiscovery.createClient(serviceName);
85
89
  }
86
90
 
91
+ /**
92
+ * Sets the app service client used to fetch manifests, configs, and settings.
93
+ * @param client_or_cb - A promise resolving to an {@link IAppClient}, or a callback
94
+ * that receives module initializer args and returns one.
95
+ */
87
96
  public setClient(
88
97
  client_or_cb:
89
98
  | Promise<AppModuleConfig['client']>
@@ -93,16 +102,30 @@ export class AppConfigurator
93
102
  this._set('client', cb);
94
103
  }
95
104
 
96
- // TODO - explain why, used in import of resources aka proxy url
105
+ /**
106
+ * Sets the base URI used to proxy-load application script bundles.
107
+ *
108
+ * TODO(#5132) - explain why, used in import of resources aka proxy url
109
+ *
110
+ * @param base_or_cb - A static URI string or a callback returning one.
111
+ */
97
112
  public setAssetUri(base_or_cb: string | ConfigBuilderCallback<string>) {
98
113
  const cb = typeof base_or_cb === 'string' ? async () => base_or_cb : base_or_cb;
99
114
  this._set('assetUri', cb);
100
115
  }
101
116
 
117
+ /**
118
+ * Builds the resolved {@link AppModuleConfig}, applying default client and asset URI
119
+ * when they haven't been explicitly configured.
120
+ * @param init - Module initializer args used to build the default client.
121
+ * @param initial - Optional initial partial configuration.
122
+ * @returns The resolved module config.
123
+ */
102
124
  protected _createConfig(
103
125
  init: ModuleInitializerArgs<IAppConfigurator, [HttpModule, ServiceDiscoveryModule]>,
104
126
  initial?: Partial<AppModuleConfig>,
105
127
  ) {
128
+ // fall back to a default client created via the http/service discovery modules
106
129
  if (!this._has('client')) {
107
130
  this.setClient(async () => {
108
131
  const httpClient = await this._createHttpClient(init);
@@ -111,6 +134,7 @@ export class AppConfigurator
111
134
  });
112
135
  }
113
136
 
137
+ // fall back to the default proxy asset uri
114
138
  if (!this._has('assetUri')) {
115
139
  this.setAssetUri('/apps-proxy');
116
140
  }
@@ -45,6 +45,7 @@ export class AppModuleProvider {
45
45
  /**
46
46
  * Shallow-compares two app manifests by JSON serialization.
47
47
  *
48
+ * @template T - The manifest type being compared.
48
49
  * @param a - First manifest to compare.
49
50
  * @param b - Second manifest to compare.
50
51
  * @returns `true` if the serialized manifests are identical.
@@ -65,6 +66,7 @@ export class AppModuleProvider {
65
66
 
66
67
  /**
67
68
  * Get module version
69
+ * @returns The module's semantic version.
68
70
  */
69
71
  get version(): SemanticVersion {
70
72
  return new SemanticVersion(version);
@@ -76,6 +78,8 @@ export class AppModuleProvider {
76
78
  * - `undefined` – no application has been set yet.
77
79
  * - `null` – the current application was explicitly cleared.
78
80
  * - `App` – an active application instance.
81
+ *
82
+ * @returns The current active application, `null` if cleared, or `undefined` if never set.
79
83
  */
80
84
  get current(): CurrentApp | null | undefined {
81
85
  return this.#current$.value;
@@ -86,10 +90,14 @@ export class AppModuleProvider {
86
90
  *
87
91
  * Emits are deduplicated by `appKey`; re-setting the same app does not trigger
88
92
  * a new emission.
93
+ *
94
+ * @returns An observable of the current active application.
89
95
  */
90
96
  get current$(): Observable<CurrentApp | null> {
97
+ // dedupe emissions when the current app's key is unchanged
91
98
  return this.#current$.pipe(
92
99
  distinctUntilChanged((prev, next) => {
100
+ // compare by appKey when both are set, otherwise fall back to reference equality
93
101
  if (prev && next) {
94
102
  return prev.appKey === next.appKey;
95
103
  }
@@ -116,6 +124,7 @@ export class AppModuleProvider {
116
124
 
117
125
  this.#subscription.add(
118
126
  this.current$
127
+ // dispatch a lifecycle event whenever the current app changes
119
128
  .pipe(
120
129
  pairwise(),
121
130
  takeWhile(() => !!event),
@@ -130,6 +139,7 @@ export class AppModuleProvider {
130
139
 
131
140
  this.#subscription.add(
132
141
  this.#current$
142
+ // dispose the previous app once it has been replaced
133
143
  .pipe(
134
144
  pairwise(),
135
145
  map(([previous]) => previous),
@@ -164,6 +174,7 @@ export class AppModuleProvider {
164
174
  /**
165
175
  * fetch all applications
166
176
  * @deprecated use `getAppManifests` instead
177
+ * @returns An observable that emits an array of {@link AppManifest} objects.
167
178
  */
168
179
  public getAllAppManifests(): Observable<AppManifest[]> {
169
180
  return this.getAppManifests();
@@ -214,12 +225,14 @@ export class AppModuleProvider {
214
225
  * @param appKeyOrApp - Application key, app reference, or an existing `IApp` instance.
215
226
  */
216
227
  public setCurrentApp(appKeyOrApp: string | IApp | AppReference): void {
228
+ // a plain string is treated as an appKey to create a new App from
217
229
  if (typeof appKeyOrApp === 'string') {
218
230
  const newApp = new App({ appKey: appKeyOrApp }, { provider: this, event: this.#event });
219
231
  this.#current$.next(newApp as CurrentApp);
220
232
  return;
221
233
  }
222
234
 
235
+ // an object with appKey and tag is treated as an AppReference to create a new App from
223
236
  if (appKeyOrApp.appKey && 'tag' in appKeyOrApp) {
224
237
  const newApp = new App(
225
238
  { appKey: appKeyOrApp.appKey, tag: appKeyOrApp.tag },
@@ -242,6 +255,7 @@ export class AppModuleProvider {
242
255
 
243
256
  /**
244
257
  * Base URI used for proxying application script imports.
258
+ * @returns The configured asset base URI.
245
259
  */
246
260
  public get assetUri(): string {
247
261
  return this.#appBaseUri;
@@ -250,6 +264,8 @@ export class AppModuleProvider {
250
264
  /**
251
265
  * This should not be used, only for legacy creation backdoor
252
266
  * @deprecated
267
+ * @param value - The initial app bundle state to construct the app from.
268
+ * @returns The newly created {@link App} instance.
253
269
  */
254
270
  public createApp(value: AppBundleStateInitial): App {
255
271
  console.warn('AppModuleProvider.createApp is deprecated and should not be used.');