@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.
- package/CHANGELOG.md +18 -0
- package/README.md +23 -0
- package/dist/esm/AppConfigurator.js +1 -1
- package/dist/esm/AppConfigurator.js.map +1 -1
- package/dist/esm/app/App.js +12 -10
- 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/esm/version.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/types/app/App.d.ts +5 -5
- package/dist/types/helpers.d.ts +1 -1
- package/dist/types/version.d.ts +1 -1
- package/package.json +9 -9
- package/src/app/App.ts +217 -31
- 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/helpers.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import { AppManifest } from './types';
|
|
2
|
-
export declare const compareAppManifest: <T extends AppManifest>(a?: T
|
|
2
|
+
export declare const compareAppManifest: <T extends AppManifest>(a?: T, b?: T) => boolean;
|
package/dist/types/version.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const version = "5.
|
|
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.
|
|
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.
|
|
52
|
-
"@equinor/fusion-query": "^4.0
|
|
51
|
+
"@equinor/fusion-observable": "^8.2.0",
|
|
52
|
+
"@equinor/fusion-query": "^4.1.0"
|
|
53
53
|
},
|
|
54
54
|
"devDependencies": {
|
|
55
|
-
"typescript": "^5.
|
|
56
|
-
"@equinor/fusion-framework-module": "^4.
|
|
57
|
-
"@equinor/fusion-framework-module
|
|
58
|
-
"@equinor/fusion-framework-module-http": "^5.
|
|
59
|
-
"@equinor/fusion-framework-module-msal": "^3.0
|
|
60
|
-
"@equinor/fusion-framework-module-service-discovery": "^7.0
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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((
|
|
409
|
+
return new Observable((subscriber) => {
|
|
410
|
+
// dispatch initialize action to indicate that the application is initializing
|
|
272
411
|
this.#state.next(actions.initialize());
|
|
273
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
421
|
-
|
|
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
|
-
|
|
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
|
};
|