@equinor/fusion-framework-module-app 5.2.13 → 5.3.0

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.
@@ -39,12 +39,12 @@ export declare class App<TEnv = any, TModules extends Array<AnyModule> | unknown
39
39
  get config$(): Observable<AppConfig<TEnv>>;
40
40
  get modules$(): Observable<AppScriptModule>;
41
41
  get instance$(): Observable<AppModulesInstance<TModules>>;
42
- get state(): AppBundleState;
42
+ get state(): Readonly<AppBundleState>;
43
43
  get appKey(): string;
44
- get manifest(): AppManifest | undefined;
45
- get manifestAsync(): Promise<AppManifest>;
46
- get config(): AppConfig<TEnv> | undefined;
47
- get configAsync(): Promise<AppConfig<TEnv>>;
44
+ get manifest(): Readonly<AppManifest> | undefined;
45
+ get manifestAsync(): Promise<Readonly<AppManifest>>;
46
+ get config(): Readonly<AppConfig<TEnv>> | undefined;
47
+ get configAsync(): Promise<Readonly<AppConfig<TEnv>>>;
48
48
  get instance(): AppModulesInstance<TModules> | undefined;
49
49
  constructor(value: AppBundleStateInitial, args: {
50
50
  provider: AppModuleProvider;
@@ -1,2 +1,2 @@
1
1
  import { AppManifest } from './types';
2
- export declare const compareAppManifest: <T extends AppManifest>(a?: T | undefined, b?: T | undefined) => boolean;
2
+ export declare const compareAppManifest: <T extends AppManifest>(a?: T, b?: T) => boolean;
@@ -1 +1 @@
1
- export declare const version = "5.2.13";
1
+ export declare const version = "5.3.0";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@equinor/fusion-framework-module-app",
3
- "version": "5.2.13",
3
+ "version": "5.3.0",
4
4
  "description": "",
5
5
  "main": "dist/esm/index.js",
6
6
  "exports": {
@@ -48,16 +48,16 @@
48
48
  "dependencies": {
49
49
  "immer": "^9.0.16",
50
50
  "rxjs": "^7.8.1",
51
- "@equinor/fusion-observable": "^8.1.5",
52
- "@equinor/fusion-query": "^4.0.6"
51
+ "@equinor/fusion-observable": "^8.2.0",
52
+ "@equinor/fusion-query": "^4.1.0"
53
53
  },
54
54
  "devDependencies": {
55
- "typescript": "^5.1.3",
56
- "@equinor/fusion-framework-module": "^4.2.6",
57
- "@equinor/fusion-framework-module-event": "^4.0.7",
58
- "@equinor/fusion-framework-module-http": "^5.1.5",
59
- "@equinor/fusion-framework-module-msal": "^3.0.9",
60
- "@equinor/fusion-framework-module-service-discovery": "^7.0.19"
55
+ "typescript": "^5.4.2",
56
+ "@equinor/fusion-framework-module-event": "^4.1.0",
57
+ "@equinor/fusion-framework-module": "^4.3.0",
58
+ "@equinor/fusion-framework-module-http": "^5.2.0",
59
+ "@equinor/fusion-framework-module-msal": "^3.1.0",
60
+ "@equinor/fusion-framework-module-service-discovery": "^7.1.0"
61
61
  },
62
62
  "scripts": {
63
63
  "build": "tsc -b"
package/src/app/App.ts CHANGED
@@ -8,7 +8,6 @@ import {
8
8
  firstValueFrom,
9
9
  lastValueFrom,
10
10
  map,
11
- of,
12
11
  OperatorFunction,
13
12
  Subscription,
14
13
  } from 'rxjs';
@@ -25,39 +24,150 @@ export function filterEmpty<T>(): OperatorFunction<T | null | undefined, T> {
25
24
  return filter((value): value is T => value !== undefined && value !== null);
26
25
  }
27
26
 
28
- // TODO add comments!
27
+ /**
28
+ * Represents an application in the framework.
29
+ * @template TEnv The type of the environment.
30
+ * @template TModules The type of the app modules.
31
+ */
29
32
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
30
33
  export interface IApp<TEnv = any, TModules extends Array<AnyModule> | unknown = unknown> {
34
+ /**
35
+ * Returns an observable that emits the app manifest.
36
+ * @returns An observable of type AppManifest.
37
+ */
31
38
  get manifest$(): Observable<AppManifest>;
39
+
40
+ /**
41
+ * Observable that emits the configuration of the app.
42
+ * @returns An Observable that emits the app configuration.
43
+ */
32
44
  get config$(): Observable<AppConfig<TEnv>>;
45
+
46
+ /**
47
+ * Returns an observable stream of the loaded app script instance.
48
+ * @returns {Observable<AppScriptModule>} The observable stream of app script modules.
49
+ */
33
50
  get modules$(): Observable<AppScriptModule>;
51
+
52
+ /**
53
+ * Returns an observable that emits the instance of the app modules.
54
+ * @returns An observable that emits the instance of the app modules.
55
+ */
34
56
  get instance$(): Observable<AppModulesInstance<TModules>>;
57
+
58
+ /**
59
+ * Gets the current state of the Application.
60
+ * @returns The current state of the Application.
61
+ */
35
62
  get state(): AppBundleState;
63
+
64
+ /**
65
+ * Gets the app key.
66
+ * @returns The app key.
67
+ */
36
68
  get appKey(): string;
69
+
70
+ /**
71
+ * Gets the manifest of the app.
72
+ * @returns The manifest of the app, or undefined if it doesn't exist.
73
+ */
37
74
  get manifest(): AppManifest | undefined;
75
+
76
+ /**
77
+ * Retrieves the manifest asynchronously.
78
+ * @returns A promise that resolves to the AppManifest.
79
+ */
38
80
  get manifestAsync(): Promise<AppManifest>;
81
+
82
+ /**
83
+ * Gets the configuration of the app.
84
+ * @returns The configuration object or undefined if no configuration is set.
85
+ */
39
86
  get config(): AppConfig<TEnv> | undefined;
87
+
88
+ /**
89
+ * Retrieves the configuration asynchronously.
90
+ * @returns A promise that resolves to the AppConfig.
91
+ */
40
92
  get instance(): AppModulesInstance<TModules> | undefined;
41
93
 
94
+ /**
95
+ * Initializes the application container.
96
+ * @returns An observable that emits an object containing the manifest, script, and config.
97
+ * @example
98
+ * ```typescript
99
+ * app.initialize().subscribe({
100
+ * next: ({ manifest, script, config }) => {
101
+ * // Use the manifest, script, and config to initialize the application
102
+ * script.render(el, ...);
103
+ * },
104
+ * error: (err) => console.error('Failed to load application', err),
105
+ * complete: () => setInitializingApp(false)
106
+ * });
107
+ * ```
108
+ */
42
109
  initialize(): Observable<{
43
110
  manifest: AppManifest;
44
111
  script: AppScriptModule;
45
112
  config: AppConfig;
46
113
  }>;
47
114
 
115
+ /**
116
+ * Loads the app configuration.
117
+ */
48
118
  loadConfig(): void;
49
119
 
120
+ /**
121
+ * Loads the app manifest.
122
+ */
50
123
  loadManifest(): void;
51
124
 
125
+ /**
126
+ * Loads the app module.
127
+ * @param allow_cache Whether to allow loading from cache.
128
+ */
52
129
  loadAppModule(allow_cache?: boolean): void;
53
130
 
131
+ /**
132
+ * Gets the app configuration.
133
+ * @param force_refresh Whether to force refreshing the configuration.
134
+ * @returns An observable that emits the app configuration.
135
+ */
54
136
  getConfig(force_refresh?: boolean): Observable<AppConfig>;
137
+
138
+ /**
139
+ * Retrieves the app configuration asynchronously.
140
+ * @param allow_cache Whether to allow loading from cache.
141
+ * @returns A promise that resolves to the AppConfig.
142
+ */
55
143
  getConfigAsync(allow_cache?: boolean): Promise<AppConfig>;
56
144
 
145
+ /**
146
+ * Gets the app manifest.
147
+ * @param force_refresh Whether to force refreshing the manifest.
148
+ * @returns An observable that emits the app manifest.
149
+ */
57
150
  getManifest(force_refresh?: boolean): Observable<AppManifest>;
151
+
152
+ /**
153
+ * Retrieves the app manifest asynchronously.
154
+ * @param allow_cache Whether to allow loading from cache.
155
+ * @returns A promise that resolves to the AppManifest.
156
+ */
58
157
  getManifestAsync(allow_cache?: boolean): Promise<AppManifest>;
59
158
 
159
+ /**
160
+ * Gets the app module.
161
+ * @param force_refresh Whether to force refreshing the app module.
162
+ * @returns An observable that emits the app module.
163
+ */
60
164
  getAppModule(force_refresh?: boolean): Observable<AppScriptModule>;
165
+
166
+ /**
167
+ * Retrieves the app module asynchronously.
168
+ * @param allow_cache Whether to allow loading from cache.
169
+ * @returns A promise that resolves to the AppScriptModule.
170
+ */
61
171
  getAppModuleAsync(allow_cache?: boolean): Promise<AppScriptModule>;
62
172
  }
63
173
 
@@ -100,28 +210,28 @@ export class App<TEnv = any, TModules extends Array<AnyModule> | unknown = unkno
100
210
 
101
211
  //#endregion
102
212
 
103
- get state(): AppBundleState {
213
+ get state(): Readonly<AppBundleState> {
104
214
  // todo deep-freeze
105
- return this.#state.value;
215
+ return Object.freeze(this.#state.value) as Readonly<AppBundleState>;
106
216
  }
107
217
 
108
218
  get appKey(): string {
109
219
  return this.#state.value.appKey;
110
220
  }
111
221
 
112
- get manifest(): AppManifest | undefined {
222
+ get manifest(): Readonly<AppManifest> | undefined {
113
223
  return this.state.manifest;
114
224
  }
115
225
 
116
- get manifestAsync(): Promise<AppManifest> {
226
+ get manifestAsync(): Promise<Readonly<AppManifest>> {
117
227
  return firstValueFrom(this.manifest$);
118
228
  }
119
229
 
120
- get config(): AppConfig<TEnv> | undefined {
230
+ get config(): Readonly<AppConfig<TEnv>> | undefined {
121
231
  return this.state.config;
122
232
  }
123
233
 
124
- get configAsync(): Promise<AppConfig<TEnv>> {
234
+ get configAsync(): Promise<Readonly<AppConfig<TEnv>>> {
125
235
  return firstValueFrom(this.config$);
126
236
  }
127
237
 
@@ -141,16 +251,24 @@ export class App<TEnv = any, TModules extends Array<AnyModule> | unknown = unkno
141
251
  const { appKey } = value;
142
252
  const { event } = args;
143
253
 
254
+ // register events if event module is provided
144
255
  event && this.#registerEvents(event);
145
256
 
257
+ // create a tear down handler for the application
146
258
  const subscriptions = new Subscription();
259
+
147
260
  if (event) {
261
+ // when app is disposed, dispatch event to notify listeners
148
262
  subscriptions.add(() => {
149
263
  event.dispatchEvent('onAppDispose', { detail: { appKey } });
150
264
  });
265
+
266
+ // when disposed, dispose of monitoring of app modules loaded
151
267
  subscriptions.add(
152
268
  event.addEventListener('onAppModulesLoaded', (e) => {
269
+ // validate that the event is for the current app
153
270
  if (e.detail.appKey === appKey) {
271
+ // set the instance of the app modules
154
272
  this.#state.next(actions.setInstance(e.detail.modules));
155
273
  }
156
274
  }),
@@ -160,88 +278,122 @@ export class App<TEnv = any, TModules extends Array<AnyModule> | unknown = unkno
160
278
  this.dispose = () => {
161
279
  subscriptions?.unsubscribe();
162
280
  if (this.#state.value.instance) {
163
- /** tear down modules of application */
281
+ // tear down modules of application
164
282
  this.#state.value.instance.dispose();
165
283
  }
166
284
  this.#state.complete();
167
285
  };
168
286
  }
169
287
 
288
+ /**
289
+ * Registers event listeners for various actions in the app.
290
+ * @param event - The event module used for dispatching events.
291
+ */
170
292
  #registerEvents(event: ModuleType<EventModule>): void {
171
293
  const { appKey } = this;
172
294
 
295
+ // monitor when application manifest is loading
173
296
  this.#state.addEffect(actions.fetchManifest.type, () => {
297
+ // dispatch event to notify listeners that the application manifest is being loaded
174
298
  event.dispatchEvent('onAppManifestLoad', {
175
299
  detail: { appKey },
176
300
  source: this,
177
301
  });
178
302
  });
303
+
304
+ // monitor when application manifest is loaded
179
305
  this.#state.addEffect(actions.fetchManifest.success.type, (action) => {
306
+ // dispatch event to notify listeners that the application manifest has been loaded
180
307
  event.dispatchEvent('onAppManifestLoaded', {
181
308
  detail: { appKey, manifest: action.payload },
182
309
  source: this,
183
310
  });
184
311
  });
312
+
313
+ // monitor when application manifest fails to load
185
314
  this.#state.addEffect(actions.fetchManifest.failure.type, (action) => {
315
+ // dispatch event to notify listeners that the application manifest failed to load
186
316
  event.dispatchEvent('onAppManifestFailure', {
187
317
  detail: { appKey, error: action.payload },
188
318
  source: this,
189
319
  });
190
320
  });
191
321
 
322
+ // monitor when application configuration is loading
192
323
  this.#state.addEffect(actions.fetchConfig.type, () => {
324
+ // dispatch event to notify listeners that the application configuration is being loaded
193
325
  event.dispatchEvent('onAppConfigLoad', {
194
326
  detail: { appKey },
195
327
  source: this,
196
328
  });
197
329
  });
330
+
331
+ // monitor when application configuration is loaded
198
332
  this.#state.addEffect(actions.fetchConfig.success.type, (action) => {
333
+ // dispatch event to notify listeners that the application configuration has been loaded
199
334
  event.dispatchEvent('onAppConfigLoaded', {
200
335
  detail: { appKey, config: action.payload },
201
336
  source: this,
202
337
  });
203
338
  });
339
+
340
+ // monitor when application configuration fails to load
204
341
  this.#state.addEffect(actions.fetchConfig.failure.type, (action) => {
342
+ // dispatch event to notify listeners that the application configuration failed to load
205
343
  event.dispatchEvent('onAppConfigFailure', {
206
344
  detail: { appKey, error: action.payload },
207
345
  source: this,
208
346
  });
209
347
  });
210
348
 
349
+ // monitor when application script is loading
211
350
  this.#state.addEffect(actions.importApp.type, () => {
351
+ // dispatch event to notify listeners that the application script is being loaded
212
352
  event.dispatchEvent('onAppScriptLoad', {
213
353
  detail: { appKey },
214
354
  source: this,
215
355
  });
216
356
  });
357
+
358
+ // monitor when application script is loaded
217
359
  this.#state.addEffect(actions.importApp.success.type, (action) => {
360
+ // dispatch event to notify listeners that the application script has been loaded
218
361
  event.dispatchEvent('onAppScriptLoaded', {
219
362
  detail: { appKey, script: action.payload },
220
363
  source: this,
221
364
  });
222
365
  });
366
+
367
+ // monitor when application script fails to load
223
368
  this.#state.addEffect(actions.importApp.failure.type, (action) => {
369
+ // dispatch event to notify listeners that the application script failed to load
224
370
  event.dispatchEvent('onAppScriptFailure', {
225
371
  detail: { appKey, error: action.payload },
226
372
  source: this,
227
373
  });
228
374
  });
229
375
 
376
+ // monitor when application is initializing
230
377
  this.#state.addEffect(actions.initialize.type, () => {
378
+ // dispatch event to notify listeners that the application is initializing
231
379
  event.dispatchEvent('onAppInitialize', {
232
380
  detail: { appKey },
233
381
  source: this,
234
382
  });
235
383
  });
236
384
 
385
+ // monitor when application has been initialized
237
386
  this.#state.addEffect(actions.initialize.success.type, () => {
387
+ // dispatch event to notify listeners that the application has been initialized
238
388
  event.dispatchEvent('onAppInitialized', {
239
389
  detail: { appKey },
240
390
  source: this,
241
391
  });
242
392
  });
243
393
 
394
+ // monitor when application fails to initialize
244
395
  this.#state.addEffect(actions.initialize.failure.type, ({ payload }) => {
396
+ // dispatch event to notify listeners that the application failed to initialize
245
397
  event.dispatchEvent('onAppInitializeFailure', {
246
398
  detail: { appKey, error: payload },
247
399
  source: this,
@@ -249,45 +401,36 @@ export class App<TEnv = any, TModules extends Array<AnyModule> | unknown = unkno
249
401
  });
250
402
  }
251
403
 
252
- /**
253
- * The initializing request won`t trigger until subscribing to the returned observable
254
- *
255
- * @example
256
- * ```ts
257
- * app.initialize().subscribe({
258
- * next: (value) => {
259
- * value.script.render(el, ...);
260
- * },
261
- * error: (err) => console.error('failed to load application', err),
262
- * complete: () => setInitializingApp(false)
263
- * })
264
- * ```
265
- */
266
404
  public initialize(): Observable<{
267
405
  manifest: AppManifest;
268
406
  script: AppScriptModule;
269
407
  config: AppConfig;
270
408
  }> {
271
- return new Observable((observer) => {
409
+ return new Observable((subscriber) => {
410
+ // dispatch initialize action to indicate that the application is initializing
272
411
  this.#state.next(actions.initialize());
273
- observer.add(
412
+ subscriber.add(
413
+ // request latest manifest, application script, and configuration
274
414
  combineLatest([
275
415
  this.getManifest(),
276
416
  this.getAppModule(),
277
417
  this.getConfig(),
278
418
  ]).subscribe({
279
419
  next: ([manifest, script, config]) =>
280
- observer.next({
420
+ // emit the manifest, script, and config to the subscriber
421
+ subscriber.next({
281
422
  manifest,
282
423
  script,
283
424
  config,
284
425
  }),
285
426
  error: (err) => {
286
- observer.error(err), this.#state.next(actions.initialize.failure(err));
427
+ // emit error and complete the stream
428
+ subscriber.error(err), this.#state.next(actions.initialize.failure(err));
287
429
  },
288
430
  complete: () => {
431
+ // dispatch initialize success action to indicate that the application has been initialized
289
432
  this.#state.next(actions.initialize.success());
290
- observer.complete();
433
+ subscriber.complete();
291
434
  },
292
435
  }),
293
436
  );
@@ -314,24 +457,37 @@ export class App<TEnv = any, TModules extends Array<AnyModule> | unknown = unkno
314
457
  public getConfig(force_refresh = false): Observable<AppConfig> {
315
458
  return new Observable((subscriber) => {
316
459
  if (this.#state.value.config) {
460
+ // emit current config to the subscriber
317
461
  subscriber.next(this.#state.value.config);
318
462
  if (!force_refresh) {
463
+ // since we have the config and no force refresh, complete the stream
319
464
  return subscriber.complete();
320
465
  }
321
466
  }
467
+
468
+ // when stream closes, dispose of subscription to change of state config
322
469
  subscriber.add(
470
+ // monitor changes to state changes of config and emit to subscriber
323
471
  this.#state.addEffect('set_config', ({ payload }) => {
324
472
  subscriber.next(payload);
325
473
  }),
326
474
  );
475
+
476
+ // when stream closes, dispose of subscription to fetch config
327
477
  subscriber.add(
478
+ // monitor success of fetching config and emit to subscriber
328
479
  this.#state.addEffect('fetch_config::success', ({ payload }) => {
480
+ // application config loaded, emit to subscriber and complete the stream
329
481
  subscriber.next(payload);
330
482
  subscriber.complete();
331
483
  }),
332
484
  );
485
+
486
+ // when stream closes, dispose of subscription to fetch config
333
487
  subscriber.add(
488
+ // monitor failure of fetching config and emit error to subscriber
334
489
  this.#state.addEffect('fetch_config::failure', ({ payload }) => {
490
+ // application config failed to load, emit error and complete the stream
335
491
  subscriber.error(
336
492
  Error('failed to load application config', {
337
493
  cause: payload,
@@ -345,6 +501,7 @@ export class App<TEnv = any, TModules extends Array<AnyModule> | unknown = unkno
345
501
  }
346
502
 
347
503
  public getConfigAsync(allow_cache = true): Promise<AppConfig> {
504
+ // when allow_cache is true, use first emitted value, otherwise use last emitted value
348
505
  const operator = allow_cache ? firstValueFrom : lastValueFrom;
349
506
  return operator(this.getConfig(!allow_cache));
350
507
  }
@@ -352,24 +509,34 @@ export class App<TEnv = any, TModules extends Array<AnyModule> | unknown = unkno
352
509
  public getManifest(force_refresh = false): Observable<AppManifest> {
353
510
  return new Observable((subscriber) => {
354
511
  if (this.#state.value.manifest) {
512
+ // emit current manifest to the subscriber
355
513
  subscriber.next(this.#state.value.manifest);
356
514
  if (!force_refresh) {
515
+ // since we have the manifest and no force refresh, complete the stream
357
516
  return subscriber.complete();
358
517
  }
359
518
  }
519
+ // when stream closes, dispose of subscription to change of state manifest
360
520
  subscriber.add(
521
+ // monitor changes to state changes of manifest and emit to subscriber
361
522
  this.#state.addEffect('set_manifest', ({ payload }) => {
362
523
  subscriber.next(payload);
363
524
  }),
364
525
  );
526
+
527
+ // when stream closes, dispose of subscription to fetch manifest
365
528
  subscriber.add(
529
+ // monitor success of fetching manifest and emit to subscriber
366
530
  this.#state.addEffect('fetch_manifest::success', ({ payload }) => {
367
531
  subscriber.next(payload);
532
+ // application manifest loaded, complete the stream
368
533
  subscriber.complete();
369
534
  }),
370
535
  );
371
536
  subscriber.add(
537
+ // monitor failure of fetching manifest and emit error to subscriber
372
538
  this.#state.addEffect('fetch_manifest::failure', ({ payload }) => {
539
+ // application manifest failed to load, emit error and complete the stream
373
540
  subscriber.error(
374
541
  Error('failed to load application manifest', {
375
542
  cause: payload,
@@ -378,11 +545,13 @@ export class App<TEnv = any, TModules extends Array<AnyModule> | unknown = unkno
378
545
  }),
379
546
  );
380
547
 
548
+ // fetch the application manifest
381
549
  this.loadManifest();
382
550
  });
383
551
  }
384
552
 
385
553
  public getManifestAsync(allow_cache = true): Promise<AppManifest> {
554
+ // when allow_cache is true, use first emitted value, otherwise use last emitted value
386
555
  const operator = allow_cache ? firstValueFrom : lastValueFrom;
387
556
  return operator(this.getManifest(!allow_cache));
388
557
  }
@@ -390,24 +559,37 @@ export class App<TEnv = any, TModules extends Array<AnyModule> | unknown = unkno
390
559
  public getAppModule(force_refresh = false): Observable<AppScriptModule> {
391
560
  return new Observable((subscriber) => {
392
561
  if (this.#state.value.modules) {
562
+ // emit current value to the subscriber
393
563
  subscriber.next(this.#state.value.modules);
394
564
  if (!force_refresh) {
565
+ // complete if no force refresh
395
566
  return subscriber.complete();
396
567
  }
397
568
  }
569
+
570
+ // when stream closes, dispose of subscription to change of state modules
398
571
  subscriber.add(
572
+ // monitor changes to state changes of modules and emit to subscriber
399
573
  this.#state.addEffect('set_module', ({ payload }) => {
400
574
  subscriber.next(payload);
401
575
  }),
402
576
  );
577
+
578
+ // when stream closes, dispose of subscription script load success
403
579
  subscriber.add(
580
+ // monitor success of loading application script and emit to subscriber
404
581
  this.#state.addEffect('import_app::success', ({ payload }) => {
405
582
  subscriber.next(payload);
583
+ // application module loaded, complete the stream
406
584
  subscriber.complete();
407
585
  }),
408
586
  );
587
+
588
+ // when stream closes, dispose of subscription to script load failure
409
589
  subscriber.add(
590
+ // monitor failure of loading application script and emit error to subscriber
410
591
  this.#state.addEffect('import_app::failure', ({ payload }) => {
592
+ // application module failed to load, emit error and complete the stream
411
593
  subscriber.error(
412
594
  Error('failed to load application modules from script', {
413
595
  cause: payload,
@@ -416,15 +598,19 @@ export class App<TEnv = any, TModules extends Array<AnyModule> | unknown = unkno
416
598
  }),
417
599
  );
418
600
 
601
+ // when stream closes, dispose of subscription to fetch manifest
419
602
  subscriber.add(
420
- this.getManifest().subscribe((manifest) =>
421
- of(this.#state.next(actions.importApp(manifest.entry))),
422
- ),
603
+ // fetch application latest manifest and request loading of the application script
604
+ this.getManifest().subscribe((manifest) => {
605
+ // dispatch import_app action to load the application script
606
+ this.#state.next(actions.importApp(manifest.entry));
607
+ }),
423
608
  );
424
609
  });
425
610
  }
426
611
 
427
612
  public getAppModuleAsync(allow_cache = true): Promise<AppScriptModule> {
613
+ // when allow_cache is true, use first emitted value, otherwise use last emitted value
428
614
  const operator = allow_cache ? firstValueFrom : lastValueFrom;
429
615
  return operator(this.getAppModule(!allow_cache));
430
616
  }
@@ -16,6 +16,7 @@ import { AppBundleState, AppBundleStateInitial } from './types';
16
16
  export const createReducer = (value: AppBundleStateInitial) =>
17
17
  makeReducer({ ...value, status: new Set() } as AppBundleState, (builder) =>
18
18
  builder
19
+ // update or set manifest
19
20
  .addCase(actions.setManifest, (state, action) => {
20
21
  if (action.meta.update) {
21
22
  state.manifest = { ...state.manifest, ...action.payload };
@@ -32,11 +33,11 @@ export const createReducer = (value: AppBundleStateInitial) =>
32
33
  .addCase(actions.setInstance, (state, action) => {
33
34
  state.instance = action.payload;
34
35
  })
35
- /** mark status as loading {{type}} */
36
+ // add status which indicates that a request is in progress
36
37
  .addMatcher(isRequestAction, (state, action) => {
37
38
  state.status.add(actionBaseType(action));
38
39
  })
39
- /** clear status {{type}} */
40
+ // remove status when a request is complete
40
41
  .addMatcher(isCompleteAction, (state, action) => {
41
42
  state.status.delete(actionBaseType(action));
42
43
  }),
@@ -13,9 +13,18 @@ export const createState = (
13
13
  provider: AppModuleProvider,
14
14
  ): FlowSubject<AppBundleState, Actions> => {
15
15
  const reducer = createReducer(value);
16
+
17
+ // create state
16
18
  const state = new FlowSubject<AppBundleState, Actions>(reducer);
19
+
20
+ // add handler for fetching manifest
17
21
  state.addFlow(handleFetchManifest(provider));
22
+
23
+ // add handler for fetching config
18
24
  state.addFlow(handleFetchConfig(provider));
25
+
26
+ // add handler for loading application script
19
27
  state.addFlow(handleImportApplication());
28
+
20
29
  return state;
21
30
  };