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