@equinor/fusion-framework-module-app 8.1.0-next.0 → 8.1.1
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/dist/esm/schemas.js +2 -0
- package/dist/esm/schemas.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/version.d.ts +1 -1
- package/package.json +12 -9
- package/CHANGELOG.md +0 -1478
- package/src/AppClient.ts +0 -409
- package/src/AppConfig.ts +0 -99
- package/src/AppConfigSelector.ts +0 -25
- package/src/AppConfigurator.ts +0 -145
- package/src/AppModuleProvider.ts +0 -283
- package/src/__tests__/AppModuleProvider.test.ts +0 -47
- package/src/__tests__/MockAppClient.test.ts +0 -97
- package/src/app/App.ts +0 -984
- package/src/app/actions.ts +0 -112
- package/src/app/create-reducer.ts +0 -62
- package/src/app/create-state.ts +0 -52
- package/src/app/events.ts +0 -108
- package/src/app/filter-empty.ts +0 -11
- package/src/app/flows/handle-fetch-config.ts +0 -48
- package/src/app/flows/handle-fetch-manifest.ts +0 -53
- package/src/app/flows/handle-fetch-settings.ts +0 -50
- package/src/app/flows/handle-import-application.ts +0 -37
- package/src/app/flows/handle-update-settings.ts +0 -39
- package/src/app/flows/index.ts +0 -5
- package/src/app/index.ts +0 -10
- package/src/app/types.ts +0 -51
- package/src/enable-app-module.ts +0 -40
- package/src/errors/AppBuildError.ts +0 -47
- package/src/errors/AppConfigError.ts +0 -47
- package/src/errors/AppManifestError.ts +0 -47
- package/src/errors/AppScriptModuleError.ts +0 -20
- package/src/errors/AppSettingsError.ts +0 -47
- package/src/errors/app-error-type.ts +0 -9
- package/src/errors.ts +0 -6
- package/src/events.ts +0 -18
- package/src/index.ts +0 -40
- package/src/mock/MockAppClient.ts +0 -77
- package/src/mock/index.ts +0 -13
- package/src/module.ts +0 -59
- package/src/schemas.ts +0 -179
- package/src/types.ts +0 -263
- package/src/version.ts +0 -2
- package/tsconfig.json +0 -30
- package/vitest.config.ts +0 -11
package/src/app/App.ts
DELETED
|
@@ -1,984 +0,0 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
AppModulesInstance,
|
|
3
|
-
AppScriptModule,
|
|
4
|
-
AppManifest,
|
|
5
|
-
AppConfig,
|
|
6
|
-
AppSettings,
|
|
7
|
-
ConfigEnvironment,
|
|
8
|
-
} from '../types';
|
|
9
|
-
import { type FlowSubject, Observable } from '@equinor/fusion-observable';
|
|
10
|
-
|
|
11
|
-
import type { AppModuleProvider } from '../AppModuleProvider';
|
|
12
|
-
import { combineLatest, of, Subscription, firstValueFrom, lastValueFrom } from 'rxjs';
|
|
13
|
-
import { defaultIfEmpty, last, map, switchMap } from 'rxjs/operators';
|
|
14
|
-
|
|
15
|
-
import type { EventModule } from '@equinor/fusion-framework-module-event';
|
|
16
|
-
import type { AnyModule, ModuleType } from '@equinor/fusion-framework-module';
|
|
17
|
-
import { createState } from './create-state';
|
|
18
|
-
import { actions, type Actions } from './actions';
|
|
19
|
-
import type { AppBundleState, AppBundleStateInitial } from './types';
|
|
20
|
-
|
|
21
|
-
import isEqual from 'fast-deep-equal';
|
|
22
|
-
|
|
23
|
-
import { filterEmpty } from './filter-empty';
|
|
24
|
-
|
|
25
|
-
import './events';
|
|
26
|
-
|
|
27
|
-
export { filterEmpty } from './filter-empty';
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* Public interface for a single loaded Fusion application.
|
|
31
|
-
*
|
|
32
|
-
* Provides reactive observables and imperative methods for accessing the
|
|
33
|
-
* application's manifest, configuration, per-user settings, script module,
|
|
34
|
-
* and initialized module instance.
|
|
35
|
-
*
|
|
36
|
-
* @template TEnv - Shape of the environment configuration record.
|
|
37
|
-
* @template TModules - Additional framework modules the app depends on.
|
|
38
|
-
*/
|
|
39
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
40
|
-
export interface IApp<
|
|
41
|
-
TEnv extends ConfigEnvironment = ConfigEnvironment,
|
|
42
|
-
TModules extends Array<AnyModule> | unknown = unknown,
|
|
43
|
-
> {
|
|
44
|
-
/**
|
|
45
|
-
* Returns an observable that emits the app manifest.
|
|
46
|
-
* @returns An observable of type AppManifest.
|
|
47
|
-
*/
|
|
48
|
-
get manifest$(): Observable<AppManifest>;
|
|
49
|
-
|
|
50
|
-
/**
|
|
51
|
-
* Observable that emits the configuration of the app.
|
|
52
|
-
* @returns An Observable that emits the app configuration.
|
|
53
|
-
*/
|
|
54
|
-
get config$(): Observable<AppConfig<TEnv>>;
|
|
55
|
-
|
|
56
|
-
/**
|
|
57
|
-
* Returns an observable stream of the loaded app script instance.
|
|
58
|
-
* @returns {Observable<AppScriptModule>} The observable stream of app script modules.
|
|
59
|
-
*/
|
|
60
|
-
get modules$(): Observable<AppScriptModule>;
|
|
61
|
-
|
|
62
|
-
/**
|
|
63
|
-
* Returns an observable that emits the instance of the app modules.
|
|
64
|
-
* @returns An observable that emits the instance of the app modules.
|
|
65
|
-
*/
|
|
66
|
-
get instance$(): Observable<AppModulesInstance<TModules>>;
|
|
67
|
-
|
|
68
|
-
/**
|
|
69
|
-
* Observable that emits the settings of the app.
|
|
70
|
-
* @returns An Observable that emits the app settings.
|
|
71
|
-
*/
|
|
72
|
-
get settings$(): Observable<AppSettings>;
|
|
73
|
-
|
|
74
|
-
/**
|
|
75
|
-
* Observable that emits the status of the app.
|
|
76
|
-
* @returns An Observable that emits the app status.
|
|
77
|
-
*/
|
|
78
|
-
get status$(): Observable<AppBundleState['status']>;
|
|
79
|
-
|
|
80
|
-
/**
|
|
81
|
-
* Gets the current state of the Application.
|
|
82
|
-
* @returns The current state of the Application.
|
|
83
|
-
*/
|
|
84
|
-
get state(): AppBundleState;
|
|
85
|
-
|
|
86
|
-
/**
|
|
87
|
-
* Gets the app key.
|
|
88
|
-
* @returns The app key.
|
|
89
|
-
*/
|
|
90
|
-
get appKey(): string;
|
|
91
|
-
|
|
92
|
-
/**
|
|
93
|
-
* Gets the manifest of the app.
|
|
94
|
-
* @returns The manifest of the app, or undefined if it doesn't exist.
|
|
95
|
-
*/
|
|
96
|
-
get manifest(): Readonly<AppManifest> | undefined;
|
|
97
|
-
|
|
98
|
-
/**
|
|
99
|
-
* Retrieves the manifest asynchronously.
|
|
100
|
-
* @returns A promise that resolves to the AppManifest.
|
|
101
|
-
*/
|
|
102
|
-
get manifestAsync(): Promise<Readonly<AppManifest>>;
|
|
103
|
-
|
|
104
|
-
/**
|
|
105
|
-
* Gets the configuration of the app.
|
|
106
|
-
* @returns The configuration object or undefined if no configuration is set.
|
|
107
|
-
*/
|
|
108
|
-
get config(): AppConfig<TEnv> | undefined;
|
|
109
|
-
|
|
110
|
-
/**
|
|
111
|
-
* Retrieves the configuration asynchronously.
|
|
112
|
-
* @returns A promise that resolves to the AppConfig.
|
|
113
|
-
*/
|
|
114
|
-
get instance(): AppModulesInstance<TModules> | undefined;
|
|
115
|
-
|
|
116
|
-
/**
|
|
117
|
-
* Initializes the application container.
|
|
118
|
-
* @returns An observable that emits an object containing the manifest, script, and config.
|
|
119
|
-
* @example
|
|
120
|
-
* ```typescript
|
|
121
|
-
* app.initialize().subscribe({
|
|
122
|
-
* next: ({ manifest, script, config }) => {
|
|
123
|
-
* // Use the manifest, script, and config to initialize the application
|
|
124
|
-
* script.render(el, ...);
|
|
125
|
-
* },
|
|
126
|
-
* error: (err) => console.error('Failed to load application', err),
|
|
127
|
-
* complete: () => setInitializingApp(false)
|
|
128
|
-
* });
|
|
129
|
-
* ```
|
|
130
|
-
*/
|
|
131
|
-
initialize(): Observable<{
|
|
132
|
-
manifest: AppManifest;
|
|
133
|
-
script: AppScriptModule;
|
|
134
|
-
config: AppConfig;
|
|
135
|
-
}>;
|
|
136
|
-
|
|
137
|
-
/**
|
|
138
|
-
* Loads the app configuration.
|
|
139
|
-
*/
|
|
140
|
-
loadConfig(): void;
|
|
141
|
-
|
|
142
|
-
/**
|
|
143
|
-
* Loads the app manifest.
|
|
144
|
-
*/
|
|
145
|
-
loadManifest(): void;
|
|
146
|
-
|
|
147
|
-
/**
|
|
148
|
-
* Loads the app module.
|
|
149
|
-
* @param allow_cache Whether to allow loading from cache.
|
|
150
|
-
*/
|
|
151
|
-
loadAppModule(allow_cache?: boolean): void;
|
|
152
|
-
|
|
153
|
-
/**
|
|
154
|
-
* Gets the app configuration.
|
|
155
|
-
* @param force_refresh Whether to force refreshing the configuration.
|
|
156
|
-
* @returns An observable that emits the app configuration.
|
|
157
|
-
*/
|
|
158
|
-
getConfig(force_refresh?: boolean): Observable<AppConfig>;
|
|
159
|
-
|
|
160
|
-
/**
|
|
161
|
-
* Retrieves the app configuration asynchronously.
|
|
162
|
-
* @param allow_cache Whether to allow loading from cache.
|
|
163
|
-
* @returns A promise that resolves to the AppConfig.
|
|
164
|
-
*/
|
|
165
|
-
getConfigAsync(allow_cache?: boolean): Promise<AppConfig>;
|
|
166
|
-
|
|
167
|
-
/**
|
|
168
|
-
* Gets the app settings.
|
|
169
|
-
* @param force_refresh Whether to force refreshing the settings.
|
|
170
|
-
* @returns An observable that emits the app settings.
|
|
171
|
-
*/
|
|
172
|
-
getSettings(force_refresh?: boolean): Observable<AppSettings>;
|
|
173
|
-
|
|
174
|
-
/**
|
|
175
|
-
* Retrieves the app settings asynchronously.
|
|
176
|
-
* @param allow_cache Whether to allow loading from cache.
|
|
177
|
-
* @returns A promise that resolves to the AppSettings.
|
|
178
|
-
*/
|
|
179
|
-
getSettingsAsync(allow_cache?: boolean): Promise<AppSettings>;
|
|
180
|
-
|
|
181
|
-
/**
|
|
182
|
-
* Sets the app settings.
|
|
183
|
-
* @param settings The settings object to save.
|
|
184
|
-
* @returns An observable that emits the app settings.
|
|
185
|
-
*/
|
|
186
|
-
updateSettings<T extends AppSettings>(settings: T): Observable<T>;
|
|
187
|
-
|
|
188
|
-
/**
|
|
189
|
-
* Sets the app settings asyncronously.
|
|
190
|
-
* @param settings The settings object to save.
|
|
191
|
-
* @returns An Promise that resolves the app settings.
|
|
192
|
-
*/
|
|
193
|
-
updateSettingsAsync<T extends AppSettings>(settings: T): Promise<T>;
|
|
194
|
-
|
|
195
|
-
/**
|
|
196
|
-
* Updates a specific setting of the app.
|
|
197
|
-
* @param property The property to update.
|
|
198
|
-
* @param value The value to set.
|
|
199
|
-
* @returns An observable that emits the app settings.
|
|
200
|
-
*/
|
|
201
|
-
updateSetting<T extends AppSettings, P extends keyof T>(
|
|
202
|
-
property: P,
|
|
203
|
-
value: T[P],
|
|
204
|
-
): Observable<T[P]>;
|
|
205
|
-
|
|
206
|
-
/**
|
|
207
|
-
* Updates a specific setting of the app asynchronously.
|
|
208
|
-
* @param property The property to update.
|
|
209
|
-
* @param value The value to set.
|
|
210
|
-
* @returns A promise that resolves to the AppSettings.
|
|
211
|
-
*/
|
|
212
|
-
updateSettingAsync<T extends AppSettings, P extends keyof T>(
|
|
213
|
-
property: P,
|
|
214
|
-
value: T[P],
|
|
215
|
-
): Promise<T[P]>;
|
|
216
|
-
|
|
217
|
-
/**
|
|
218
|
-
* Gets the app manifest.
|
|
219
|
-
* @param force_refresh Whether to force refreshing the manifest.
|
|
220
|
-
* @returns An observable that emits the app manifest.
|
|
221
|
-
*/
|
|
222
|
-
getManifest(force_refresh?: boolean): Observable<AppManifest>;
|
|
223
|
-
|
|
224
|
-
/**
|
|
225
|
-
* Retrieves the app manifest asynchronously.
|
|
226
|
-
* @param allow_cache Whether to allow loading from cache.
|
|
227
|
-
* @returns A promise that resolves to the AppManifest.
|
|
228
|
-
*/
|
|
229
|
-
getManifestAsync(allow_cache?: boolean): Promise<AppManifest>;
|
|
230
|
-
|
|
231
|
-
/**
|
|
232
|
-
* Gets the app module.
|
|
233
|
-
* @param force_refresh Whether to force refreshing the app module.
|
|
234
|
-
* @returns An observable that emits the app module.
|
|
235
|
-
*/
|
|
236
|
-
getAppModule(force_refresh?: boolean): Observable<AppScriptModule>;
|
|
237
|
-
|
|
238
|
-
/**
|
|
239
|
-
* Retrieves the app module asynchronously.
|
|
240
|
-
* @param allow_cache Whether to allow loading from cache.
|
|
241
|
-
* @returns A promise that resolves to the AppScriptModule.
|
|
242
|
-
*/
|
|
243
|
-
getAppModuleAsync(allow_cache?: boolean): Promise<AppScriptModule>;
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
/** Default empty settings object used when no settings have been fetched. */
|
|
247
|
-
const fallbackSettings: AppSettings = {};
|
|
248
|
-
|
|
249
|
-
/**
|
|
250
|
-
* Result emitted by {@link IApp.initialize}, containing the resolved manifest,
|
|
251
|
-
* imported script module, and runtime configuration.
|
|
252
|
-
*/
|
|
253
|
-
export type AppInitializeResult = {
|
|
254
|
-
manifest: AppManifest;
|
|
255
|
-
script: AppScriptModule;
|
|
256
|
-
config: AppConfig;
|
|
257
|
-
};
|
|
258
|
-
|
|
259
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
260
|
-
/**
|
|
261
|
-
* Concrete implementation of {@link IApp}.
|
|
262
|
-
*
|
|
263
|
-
* Manages an internal reactive state machine ({@link FlowSubject}) that orchestrates
|
|
264
|
-
* manifest fetching, config loading, settings management, and script import. Dispatches
|
|
265
|
-
* lifecycle events through the {@link EventModule} when available.
|
|
266
|
-
*
|
|
267
|
-
* TODO(#5130): streams should be made distinct until changed from state.
|
|
268
|
-
*
|
|
269
|
-
* @template TEnv - Shape of the environment configuration record.
|
|
270
|
-
* @template TModules - Additional framework modules the app depends on.
|
|
271
|
-
*/
|
|
272
|
-
export class App<
|
|
273
|
-
TEnv extends ConfigEnvironment = ConfigEnvironment,
|
|
274
|
-
TModules extends Array<AnyModule> | unknown = unknown,
|
|
275
|
-
> implements IApp<TEnv, TModules>
|
|
276
|
-
{
|
|
277
|
-
#state: FlowSubject<AppBundleState, Actions>;
|
|
278
|
-
|
|
279
|
-
//#region === streams ===
|
|
280
|
-
|
|
281
|
-
/** @inheritdoc */
|
|
282
|
-
get manifest$(): Observable<AppManifest> {
|
|
283
|
-
// filter out null/undefined manifest states
|
|
284
|
-
return this.#state.select((state) => state.manifest).pipe(filterEmpty());
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
/** @inheritdoc */
|
|
288
|
-
get config$(): Observable<AppConfig<TEnv>> {
|
|
289
|
-
return (
|
|
290
|
-
this.#state
|
|
291
|
-
.select((state) => state.config as AppConfig<TEnv>, isEqual)
|
|
292
|
-
// filter out null/undefined config states
|
|
293
|
-
.pipe(filterEmpty())
|
|
294
|
-
);
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
/** @inheritdoc */
|
|
298
|
-
get modules$(): Observable<AppScriptModule> {
|
|
299
|
-
// filter out null/undefined module states
|
|
300
|
-
return this.#state.select((state) => state.modules).pipe(filterEmpty());
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
/** @inheritdoc */
|
|
304
|
-
get instance$(): Observable<AppModulesInstance<TModules>> {
|
|
305
|
-
return (
|
|
306
|
-
this.#state
|
|
307
|
-
.select((state) => state.instance as AppModulesInstance<TModules>)
|
|
308
|
-
// filter out null/undefined instance states
|
|
309
|
-
.pipe(filterEmpty())
|
|
310
|
-
);
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
/** @inheritdoc */
|
|
314
|
-
get settings$(): Observable<AppSettings> {
|
|
315
|
-
return new Observable<AppSettings>((subscriber) => {
|
|
316
|
-
this.#state.next(actions.fetchSettings(this.appKey));
|
|
317
|
-
subscriber.add(
|
|
318
|
-
this.#state
|
|
319
|
-
.select((state) => state.settings, isEqual)
|
|
320
|
-
// filter out null/undefined settings and fall back to the default empty settings
|
|
321
|
-
.pipe(filterEmpty(), defaultIfEmpty(fallbackSettings))
|
|
322
|
-
.subscribe(subscriber),
|
|
323
|
-
);
|
|
324
|
-
});
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
/** @inheritdoc */
|
|
328
|
-
get status$(): Observable<AppBundleState['status']> {
|
|
329
|
-
return this.#state.select((state) => state.status);
|
|
330
|
-
}
|
|
331
|
-
|
|
332
|
-
//#endregion
|
|
333
|
-
|
|
334
|
-
/** @inheritdoc */
|
|
335
|
-
get state(): Readonly<AppBundleState> {
|
|
336
|
-
// TODO(#5133) deep-freeze
|
|
337
|
-
return Object.freeze(this.#state.value) as Readonly<AppBundleState>;
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
/** @inheritdoc */
|
|
341
|
-
get appKey(): string {
|
|
342
|
-
return this.#state.value.appKey;
|
|
343
|
-
}
|
|
344
|
-
/**
|
|
345
|
-
* The tag (version) this app instance was loaded with, if any.
|
|
346
|
-
* @returns The tag string, or `undefined` if none was specified.
|
|
347
|
-
*/
|
|
348
|
-
get tag(): string | undefined {
|
|
349
|
-
return this.#state.value.tag;
|
|
350
|
-
}
|
|
351
|
-
|
|
352
|
-
/** @inheritdoc */
|
|
353
|
-
get manifest(): Readonly<AppManifest> | undefined {
|
|
354
|
-
return this.state.manifest;
|
|
355
|
-
}
|
|
356
|
-
|
|
357
|
-
/** @inheritdoc */
|
|
358
|
-
get manifestAsync(): Promise<Readonly<AppManifest>> {
|
|
359
|
-
return firstValueFrom(this.manifest$);
|
|
360
|
-
}
|
|
361
|
-
|
|
362
|
-
/** @inheritdoc */
|
|
363
|
-
get config(): AppConfig<TEnv> | undefined {
|
|
364
|
-
return this.state.config as AppConfig<TEnv>;
|
|
365
|
-
}
|
|
366
|
-
|
|
367
|
-
/**
|
|
368
|
-
* Retrieves the configuration asynchronously.
|
|
369
|
-
* @returns A promise that resolves to the {@link AppConfig}.
|
|
370
|
-
*/
|
|
371
|
-
get configAsync(): Promise<AppConfig<TEnv>> {
|
|
372
|
-
return firstValueFrom(this.config$);
|
|
373
|
-
}
|
|
374
|
-
|
|
375
|
-
/** @inheritdoc */
|
|
376
|
-
get instance(): AppModulesInstance<TModules> | undefined {
|
|
377
|
-
return this.#state.value.instance as AppModulesInstance<TModules>;
|
|
378
|
-
}
|
|
379
|
-
|
|
380
|
-
/**
|
|
381
|
-
* Creates a new {@link App} instance and starts its internal state machine.
|
|
382
|
-
* @param value - Initial app bundle state (appKey, tag, and any pre-loaded data).
|
|
383
|
-
* @param args - The owning {@link AppModuleProvider} and an optional event module for lifecycle events.
|
|
384
|
-
*/
|
|
385
|
-
constructor(
|
|
386
|
-
value: AppBundleStateInitial,
|
|
387
|
-
args: {
|
|
388
|
-
provider: AppModuleProvider;
|
|
389
|
-
event?: ModuleType<EventModule>;
|
|
390
|
-
},
|
|
391
|
-
) {
|
|
392
|
-
this.#state = createState(value, args.provider);
|
|
393
|
-
|
|
394
|
-
const { appKey } = value;
|
|
395
|
-
const { event } = args;
|
|
396
|
-
|
|
397
|
-
// register events if event module is provided
|
|
398
|
-
event && this.#registerEvents(event);
|
|
399
|
-
|
|
400
|
-
// create a tear down handler for the application
|
|
401
|
-
const subscriptions = new Subscription();
|
|
402
|
-
|
|
403
|
-
// only wire up event listeners when an event module is provided
|
|
404
|
-
if (event) {
|
|
405
|
-
// when app is disposed, dispatch event to notify listeners
|
|
406
|
-
subscriptions.add(() => {
|
|
407
|
-
event.dispatchEvent('onAppDispose', { detail: { appKey } });
|
|
408
|
-
});
|
|
409
|
-
|
|
410
|
-
// when disposed, dispose of monitoring of app modules loaded
|
|
411
|
-
subscriptions.add(
|
|
412
|
-
event.addEventListener('onAppModulesLoaded', (e) => {
|
|
413
|
-
// validate that the event is for the current app
|
|
414
|
-
if (e.detail.appKey === appKey) {
|
|
415
|
-
// set the instance of the app modules
|
|
416
|
-
this.#state.next(actions.setInstance(e.detail.modules));
|
|
417
|
-
}
|
|
418
|
-
}),
|
|
419
|
-
);
|
|
420
|
-
}
|
|
421
|
-
|
|
422
|
-
this.dispose = () => {
|
|
423
|
-
subscriptions?.unsubscribe();
|
|
424
|
-
// tear down modules of application, if an instance was loaded
|
|
425
|
-
if (this.#state.value.instance) {
|
|
426
|
-
// tear down modules of application
|
|
427
|
-
this.#state.value.instance.dispose();
|
|
428
|
-
}
|
|
429
|
-
this.#state.complete();
|
|
430
|
-
};
|
|
431
|
-
}
|
|
432
|
-
|
|
433
|
-
/**
|
|
434
|
-
* Registers event listeners for various actions in the app.
|
|
435
|
-
* @param event - The event module used for dispatching events.
|
|
436
|
-
*/
|
|
437
|
-
#registerEvents(event: ModuleType<EventModule>): void {
|
|
438
|
-
const { appKey } = this;
|
|
439
|
-
|
|
440
|
-
// monitor when application manifest is loading
|
|
441
|
-
this.#state.addEffect(actions.fetchManifest.type, () => {
|
|
442
|
-
// dispatch event to notify listeners that the application manifest is being loaded
|
|
443
|
-
event.dispatchEvent('onAppManifestLoad', {
|
|
444
|
-
detail: { appKey },
|
|
445
|
-
source: this,
|
|
446
|
-
});
|
|
447
|
-
});
|
|
448
|
-
|
|
449
|
-
// monitor when application manifest is loaded
|
|
450
|
-
this.#state.addEffect(actions.fetchManifest.success.type, (action) => {
|
|
451
|
-
// dispatch event to notify listeners that the application manifest has been loaded
|
|
452
|
-
event.dispatchEvent('onAppManifestLoaded', {
|
|
453
|
-
detail: { appKey, manifest: action.payload },
|
|
454
|
-
source: this,
|
|
455
|
-
});
|
|
456
|
-
});
|
|
457
|
-
|
|
458
|
-
// monitor when application manifest fails to load
|
|
459
|
-
this.#state.addEffect(actions.fetchManifest.failure.type, (action) => {
|
|
460
|
-
// dispatch event to notify listeners that the application manifest failed to load
|
|
461
|
-
event.dispatchEvent('onAppManifestFailure', {
|
|
462
|
-
detail: { appKey, error: action.payload },
|
|
463
|
-
source: this,
|
|
464
|
-
});
|
|
465
|
-
});
|
|
466
|
-
|
|
467
|
-
// monitor when application configuration is loading
|
|
468
|
-
this.#state.addEffect(actions.fetchConfig.type, () => {
|
|
469
|
-
// dispatch event to notify listeners that the application configuration is being loaded
|
|
470
|
-
event.dispatchEvent('onAppConfigLoad', {
|
|
471
|
-
detail: { appKey },
|
|
472
|
-
source: this,
|
|
473
|
-
});
|
|
474
|
-
});
|
|
475
|
-
|
|
476
|
-
// monitor when application configuration is loaded
|
|
477
|
-
this.#state.addEffect(actions.fetchConfig.success.type, (action) => {
|
|
478
|
-
// dispatch event to notify listeners that the application configuration has been loaded
|
|
479
|
-
event.dispatchEvent('onAppConfigLoaded', {
|
|
480
|
-
detail: { appKey, config: action.payload },
|
|
481
|
-
source: this,
|
|
482
|
-
});
|
|
483
|
-
});
|
|
484
|
-
|
|
485
|
-
// monitor when application configuration fails to load
|
|
486
|
-
this.#state.addEffect(actions.fetchConfig.failure.type, (action) => {
|
|
487
|
-
// dispatch event to notify listeners that the application configuration failed to load
|
|
488
|
-
event.dispatchEvent('onAppConfigFailure', {
|
|
489
|
-
detail: { appKey, error: action.payload },
|
|
490
|
-
source: this,
|
|
491
|
-
});
|
|
492
|
-
});
|
|
493
|
-
|
|
494
|
-
// monitor when application settings is loading
|
|
495
|
-
this.#state.addEffect(actions.fetchSettings.type, () => {
|
|
496
|
-
// dispatch event to notify listeners that the application settings is being loaded
|
|
497
|
-
event.dispatchEvent('onAppSettingsLoad', {
|
|
498
|
-
detail: { appKey },
|
|
499
|
-
source: this,
|
|
500
|
-
});
|
|
501
|
-
});
|
|
502
|
-
|
|
503
|
-
// monitor when application settings is loaded
|
|
504
|
-
this.#state.addEffect(actions.fetchSettings.success.type, (action) => {
|
|
505
|
-
// dispatch event to notify listeners that the application settings has been loaded
|
|
506
|
-
event.dispatchEvent('onAppSettingsLoaded', {
|
|
507
|
-
detail: { appKey, settings: action.payload },
|
|
508
|
-
source: this,
|
|
509
|
-
});
|
|
510
|
-
});
|
|
511
|
-
|
|
512
|
-
// monitor when application settings fails to load
|
|
513
|
-
this.#state.addEffect(actions.fetchSettings.failure.type, (action) => {
|
|
514
|
-
// dispatch event to notify listeners that the application settings failed to load
|
|
515
|
-
event.dispatchEvent('onAppSettingsFailure', {
|
|
516
|
-
detail: { appKey, error: action.payload },
|
|
517
|
-
source: this,
|
|
518
|
-
});
|
|
519
|
-
});
|
|
520
|
-
|
|
521
|
-
// monitor when application settings is updated
|
|
522
|
-
this.#state.addEffect(actions.updateSettings.type, (action) => {
|
|
523
|
-
// dispatch event to notify listeners that the application settings has been loaded
|
|
524
|
-
event.dispatchEvent('onAppSettingsUpdate', {
|
|
525
|
-
detail: { appKey, settings: action.payload.settings },
|
|
526
|
-
source: this,
|
|
527
|
-
});
|
|
528
|
-
});
|
|
529
|
-
|
|
530
|
-
// monitor when application settings is updated
|
|
531
|
-
this.#state.addEffect(actions.updateSettings.success.type, (action) => {
|
|
532
|
-
// dispatch event to notify listeners that the application settings has been loaded
|
|
533
|
-
event.dispatchEvent('onAppSettingsUpdated', {
|
|
534
|
-
detail: { appKey, settings: action.payload.settings },
|
|
535
|
-
source: this,
|
|
536
|
-
});
|
|
537
|
-
});
|
|
538
|
-
|
|
539
|
-
// monitor when application settings fails to updated
|
|
540
|
-
this.#state.addEffect(actions.updateSettings.failure.type, (action) => {
|
|
541
|
-
// dispatch event to notify listeners that the application settings has been loaded
|
|
542
|
-
event.dispatchEvent('onAppSettingsUpdateFailure', {
|
|
543
|
-
detail: { appKey, settings: action.payload },
|
|
544
|
-
source: this,
|
|
545
|
-
});
|
|
546
|
-
});
|
|
547
|
-
|
|
548
|
-
// monitor when application script is loading
|
|
549
|
-
this.#state.addEffect(actions.importApp.type, () => {
|
|
550
|
-
// dispatch event to notify listeners that the application script is being loaded
|
|
551
|
-
event.dispatchEvent('onAppScriptLoad', {
|
|
552
|
-
detail: { appKey },
|
|
553
|
-
source: this,
|
|
554
|
-
});
|
|
555
|
-
});
|
|
556
|
-
|
|
557
|
-
// monitor when application script is loaded
|
|
558
|
-
this.#state.addEffect(actions.importApp.success.type, (action) => {
|
|
559
|
-
// dispatch event to notify listeners that the application script has been loaded
|
|
560
|
-
event.dispatchEvent('onAppScriptLoaded', {
|
|
561
|
-
detail: { appKey, script: action.payload },
|
|
562
|
-
source: this,
|
|
563
|
-
});
|
|
564
|
-
});
|
|
565
|
-
|
|
566
|
-
// monitor when application script fails to load
|
|
567
|
-
this.#state.addEffect(actions.importApp.failure.type, (action) => {
|
|
568
|
-
// dispatch event to notify listeners that the application script failed to load
|
|
569
|
-
event.dispatchEvent('onAppScriptFailure', {
|
|
570
|
-
detail: { appKey, error: action.payload },
|
|
571
|
-
source: this,
|
|
572
|
-
});
|
|
573
|
-
});
|
|
574
|
-
|
|
575
|
-
// monitor when application is initializing
|
|
576
|
-
this.#state.addEffect(actions.initialize.type, () => {
|
|
577
|
-
// dispatch event to notify listeners that the application is initializing
|
|
578
|
-
event.dispatchEvent('onAppInitialize', {
|
|
579
|
-
detail: { appKey },
|
|
580
|
-
source: this,
|
|
581
|
-
});
|
|
582
|
-
});
|
|
583
|
-
|
|
584
|
-
// monitor when application has been initialized
|
|
585
|
-
this.#state.addEffect(actions.initialize.success.type, () => {
|
|
586
|
-
// dispatch event to notify listeners that the application has been initialized
|
|
587
|
-
event.dispatchEvent('onAppInitialized', {
|
|
588
|
-
detail: { appKey },
|
|
589
|
-
source: this,
|
|
590
|
-
});
|
|
591
|
-
});
|
|
592
|
-
|
|
593
|
-
// monitor when application fails to initialize
|
|
594
|
-
this.#state.addEffect(actions.initialize.failure.type, ({ payload }) => {
|
|
595
|
-
// dispatch event to notify listeners that the application failed to initialize
|
|
596
|
-
event.dispatchEvent('onAppInitializeFailure', {
|
|
597
|
-
detail: { appKey, error: payload },
|
|
598
|
-
source: this,
|
|
599
|
-
});
|
|
600
|
-
});
|
|
601
|
-
}
|
|
602
|
-
|
|
603
|
-
/** @inheritdoc */
|
|
604
|
-
public initialize(): Observable<AppInitializeResult> {
|
|
605
|
-
return new Observable((subscriber) => {
|
|
606
|
-
// dispatch initialize action to indicate that the application is initializing
|
|
607
|
-
this.#state.next(actions.initialize());
|
|
608
|
-
subscriber.add(
|
|
609
|
-
// request latest manifest, application script, and configuration
|
|
610
|
-
combineLatest([this.getManifest(), this.getAppModule(), this.getConfig()]).subscribe({
|
|
611
|
-
next: ([manifest, script, config]) =>
|
|
612
|
-
// emit the manifest, script, and config to the subscriber
|
|
613
|
-
subscriber.next({
|
|
614
|
-
manifest,
|
|
615
|
-
script,
|
|
616
|
-
config,
|
|
617
|
-
}),
|
|
618
|
-
error: (err) => {
|
|
619
|
-
// emit error and complete the stream
|
|
620
|
-
subscriber.error(err);
|
|
621
|
-
this.#state.next(actions.initialize.failure(err));
|
|
622
|
-
},
|
|
623
|
-
complete: () => {
|
|
624
|
-
// dispatch initialize success action to indicate that the application has been initialized
|
|
625
|
-
this.#state.next(actions.initialize.success());
|
|
626
|
-
subscriber.complete();
|
|
627
|
-
},
|
|
628
|
-
}),
|
|
629
|
-
);
|
|
630
|
-
});
|
|
631
|
-
}
|
|
632
|
-
|
|
633
|
-
/** @inheritdoc */
|
|
634
|
-
public loadConfig() {
|
|
635
|
-
// TODO(#5126) - shit fix
|
|
636
|
-
(this.manifest ? of(this.manifest) : this.getManifest()).subscribe({
|
|
637
|
-
next: (manifest) => {
|
|
638
|
-
this.#state.next(actions.fetchConfig(manifest));
|
|
639
|
-
},
|
|
640
|
-
});
|
|
641
|
-
}
|
|
642
|
-
|
|
643
|
-
/** @inheritdoc */
|
|
644
|
-
public loadManifest(update?: boolean) {
|
|
645
|
-
this.#state.next(actions.fetchManifest(this.appKey, this.tag, update));
|
|
646
|
-
}
|
|
647
|
-
|
|
648
|
-
/**
|
|
649
|
-
* Replaces or merges the current manifest with a new one.
|
|
650
|
-
* @param manifest - The manifest to apply.
|
|
651
|
-
* @param replace - When `false` (default), merges into the existing manifest; when omitted, behaves the same.
|
|
652
|
-
*/
|
|
653
|
-
public updateManifest(manifest: AppManifest, replace?: false) {
|
|
654
|
-
this.#state.next(actions.setManifest(manifest, !replace));
|
|
655
|
-
}
|
|
656
|
-
|
|
657
|
-
/** @inheritdoc */
|
|
658
|
-
public async loadAppModule(allow_cache = true) {
|
|
659
|
-
const manifest = await this.getManifestAsync(allow_cache);
|
|
660
|
-
// only import the app module if the manifest declares an entry point
|
|
661
|
-
if (manifest.build?.entryPoint) {
|
|
662
|
-
this.#state.next(actions.importApp(manifest.build.entryPoint));
|
|
663
|
-
} else {
|
|
664
|
-
console.warn(
|
|
665
|
-
`The ${manifest.appKey} is missing entryPoint, please upload a build for the app before continuing`,
|
|
666
|
-
);
|
|
667
|
-
}
|
|
668
|
-
}
|
|
669
|
-
|
|
670
|
-
/** @inheritdoc */
|
|
671
|
-
public getConfig(force_refresh = false): Observable<AppConfig> {
|
|
672
|
-
return new Observable((subscriber) => {
|
|
673
|
-
// emit the cached config immediately, if one is already loaded
|
|
674
|
-
if (this.#state.value.config) {
|
|
675
|
-
// emit current config to the subscriber
|
|
676
|
-
subscriber.next(this.#state.value.config);
|
|
677
|
-
// complete the stream unless a forced refresh was requested
|
|
678
|
-
if (!force_refresh) {
|
|
679
|
-
// since we have the config and no force refresh, complete the stream
|
|
680
|
-
return subscriber.complete();
|
|
681
|
-
}
|
|
682
|
-
}
|
|
683
|
-
|
|
684
|
-
// when stream closes, dispose of subscription to change of state config
|
|
685
|
-
subscriber.add(
|
|
686
|
-
// monitor changes to state changes of config and emit to subscriber
|
|
687
|
-
this.#state.addEffect('set_config', ({ payload }) => {
|
|
688
|
-
subscriber.next(payload);
|
|
689
|
-
}),
|
|
690
|
-
);
|
|
691
|
-
|
|
692
|
-
// when stream closes, dispose of subscription to fetch config
|
|
693
|
-
subscriber.add(
|
|
694
|
-
// monitor success of fetching config and emit to subscriber
|
|
695
|
-
this.#state.addEffect('fetch_config::success', ({ payload }) => {
|
|
696
|
-
// application config loaded, emit to subscriber and complete the stream
|
|
697
|
-
subscriber.next(payload);
|
|
698
|
-
subscriber.complete();
|
|
699
|
-
}),
|
|
700
|
-
);
|
|
701
|
-
|
|
702
|
-
// when stream closes, dispose of subscription to fetch config
|
|
703
|
-
subscriber.add(
|
|
704
|
-
// monitor failure of fetching config and emit error to subscriber
|
|
705
|
-
this.#state.addEffect('fetch_config::failure', ({ payload }) => {
|
|
706
|
-
// application config failed to load, emit error and complete the stream
|
|
707
|
-
subscriber.error(
|
|
708
|
-
Error('failed to load application config', {
|
|
709
|
-
cause: payload,
|
|
710
|
-
}),
|
|
711
|
-
);
|
|
712
|
-
}),
|
|
713
|
-
);
|
|
714
|
-
|
|
715
|
-
this.loadConfig();
|
|
716
|
-
});
|
|
717
|
-
}
|
|
718
|
-
|
|
719
|
-
/** @inheritdoc */
|
|
720
|
-
public getConfigAsync(allow_cache = true): Promise<AppConfig> {
|
|
721
|
-
// when allow_cache is true, use first emitted value, otherwise use last emitted value
|
|
722
|
-
const operator = allow_cache ? firstValueFrom : lastValueFrom;
|
|
723
|
-
return operator(this.getConfig(!allow_cache));
|
|
724
|
-
}
|
|
725
|
-
|
|
726
|
-
/** @inheritdoc */
|
|
727
|
-
public getSettings<T extends AppSettings>(force_refresh = false): Observable<T> {
|
|
728
|
-
return new Observable<T>((subscriber) => {
|
|
729
|
-
// emit the cached settings immediately, if already loaded
|
|
730
|
-
if (this.#state.value.settings) {
|
|
731
|
-
// emit current settings to the subscriber
|
|
732
|
-
subscriber.next(this.#state.value.settings as T);
|
|
733
|
-
// complete the stream unless a forced refresh was requested
|
|
734
|
-
if (!force_refresh) {
|
|
735
|
-
// since we have the settings and no force refresh, complete the stream
|
|
736
|
-
return subscriber.complete();
|
|
737
|
-
}
|
|
738
|
-
}
|
|
739
|
-
|
|
740
|
-
// when stream closes, dispose of subscription to change of state settings
|
|
741
|
-
subscriber.add(
|
|
742
|
-
// monitor changes to state changes of settings and emit to subscriber
|
|
743
|
-
this.#state.addEffect('set_settings', ({ payload }) => {
|
|
744
|
-
subscriber.next(payload as T);
|
|
745
|
-
}),
|
|
746
|
-
);
|
|
747
|
-
|
|
748
|
-
// when stream closes, dispose of subscription to fetch settings
|
|
749
|
-
subscriber.add(
|
|
750
|
-
// monitor success of fetching settings and emit to subscriber
|
|
751
|
-
this.#state.addEffect('fetch_settings::success', ({ payload }) => {
|
|
752
|
-
// application settings loaded, emit to subscriber and complete the stream
|
|
753
|
-
subscriber.next(payload as T);
|
|
754
|
-
subscriber.complete();
|
|
755
|
-
}),
|
|
756
|
-
);
|
|
757
|
-
|
|
758
|
-
// when stream closes, dispose of subscription to fetch settings
|
|
759
|
-
subscriber.add(
|
|
760
|
-
// monitor failure of fetching settings and emit error to subscriber
|
|
761
|
-
this.#state.addEffect('fetch_settings::failure', ({ payload }) => {
|
|
762
|
-
// application settings failed to load, emit error and complete the stream
|
|
763
|
-
subscriber.error(
|
|
764
|
-
Error('failed to load application settings', {
|
|
765
|
-
cause: payload,
|
|
766
|
-
}),
|
|
767
|
-
);
|
|
768
|
-
}),
|
|
769
|
-
);
|
|
770
|
-
|
|
771
|
-
this.#state.next(actions.fetchSettings(this.appKey));
|
|
772
|
-
});
|
|
773
|
-
}
|
|
774
|
-
|
|
775
|
-
/** @inheritdoc */
|
|
776
|
-
public getSettingsAsync<T extends AppSettings>(allow_cache = true): Promise<T> {
|
|
777
|
-
// when allow_cache is true, use first emitted value, otherwise use last emitted value
|
|
778
|
-
const operator = allow_cache ? firstValueFrom : lastValueFrom;
|
|
779
|
-
return operator(this.getSettings<T>(!allow_cache));
|
|
780
|
-
}
|
|
781
|
-
|
|
782
|
-
/** @inheritdoc */
|
|
783
|
-
public updateSettings<T extends AppSettings>(settings: T): Observable<T> {
|
|
784
|
-
return new Observable((subscriber) => {
|
|
785
|
-
subscriber.add(
|
|
786
|
-
// monitor failure of updating settings and emit error to subscriber
|
|
787
|
-
this.#state.addEffect('update_settings::failure', ({ payload }) => {
|
|
788
|
-
// request to reset settings to source state
|
|
789
|
-
this.#state.next(actions.fetchSettings(this.appKey));
|
|
790
|
-
|
|
791
|
-
// application settings failed to save, emit error and complete the stream
|
|
792
|
-
subscriber.error(
|
|
793
|
-
Error('failed to update application settings', {
|
|
794
|
-
cause: payload,
|
|
795
|
-
}),
|
|
796
|
-
);
|
|
797
|
-
}),
|
|
798
|
-
);
|
|
799
|
-
|
|
800
|
-
subscriber.add(
|
|
801
|
-
// monitor success of updating settings and emit to subscriber
|
|
802
|
-
this.#state.addEffect('update_settings::success', ({ payload }) => {
|
|
803
|
-
subscriber.next(payload as T);
|
|
804
|
-
subscriber.complete();
|
|
805
|
-
}),
|
|
806
|
-
);
|
|
807
|
-
|
|
808
|
-
// optimistic update of settings
|
|
809
|
-
this.#state.next(actions.setSettings(settings));
|
|
810
|
-
|
|
811
|
-
// request to update settings
|
|
812
|
-
this.#state.next(actions.updateSettings(this.appKey, settings));
|
|
813
|
-
});
|
|
814
|
-
}
|
|
815
|
-
|
|
816
|
-
/** @inheritdoc */
|
|
817
|
-
public updateSettingsAsync<T extends AppSettings>(settings: T): Promise<T> {
|
|
818
|
-
return lastValueFrom(this.updateSettings(settings));
|
|
819
|
-
}
|
|
820
|
-
|
|
821
|
-
/** @inheritdoc */
|
|
822
|
-
public updateSetting<T extends AppSettings, P extends keyof T>(
|
|
823
|
-
property: P,
|
|
824
|
-
value: T[P],
|
|
825
|
-
): Observable<T[P]> {
|
|
826
|
-
const currentSettings$ =
|
|
827
|
-
this.#state.value.settings === undefined
|
|
828
|
-
? // if settings are not loaded, fetch settings
|
|
829
|
-
this.getSettings()
|
|
830
|
-
// take the last emitted value
|
|
831
|
-
.pipe(last())
|
|
832
|
-
: // if settings are loaded, use current settings
|
|
833
|
-
of(this.#state.value.settings);
|
|
834
|
-
|
|
835
|
-
// merge the current settings with the new value, then persist and return the updated property
|
|
836
|
-
return currentSettings$.pipe(
|
|
837
|
-
map((settings) => ({ ...settings, [property]: value })),
|
|
838
|
-
// update settings
|
|
839
|
-
switchMap((settings) => this.updateSettings<T>(settings as T)),
|
|
840
|
-
// return the updated property
|
|
841
|
-
map((settings) => settings[property] as T[P]),
|
|
842
|
-
);
|
|
843
|
-
}
|
|
844
|
-
|
|
845
|
-
/** @inheritdoc */
|
|
846
|
-
public updateSettingAsync<T extends AppSettings, P extends keyof T>(
|
|
847
|
-
property: P,
|
|
848
|
-
value: T[P],
|
|
849
|
-
): Promise<T[P]> {
|
|
850
|
-
return lastValueFrom(this.updateSetting<T, P>(property, value));
|
|
851
|
-
}
|
|
852
|
-
|
|
853
|
-
/** @inheritdoc */
|
|
854
|
-
public getManifest(force_refresh = false): Observable<AppManifest> {
|
|
855
|
-
return new Observable((subscriber) => {
|
|
856
|
-
// emit the cached manifest immediately, if already loaded
|
|
857
|
-
if (this.#state.value.manifest) {
|
|
858
|
-
// emit current manifest to the subscriber
|
|
859
|
-
subscriber.next(this.#state.value.manifest);
|
|
860
|
-
// complete the stream unless a forced refresh was requested
|
|
861
|
-
if (!force_refresh) {
|
|
862
|
-
// since we have the manifest and no force refresh, complete the stream
|
|
863
|
-
return subscriber.complete();
|
|
864
|
-
}
|
|
865
|
-
}
|
|
866
|
-
// when stream closes, dispose of subscription to change of state manifest
|
|
867
|
-
subscriber.add(
|
|
868
|
-
// monitor changes to state changes of manifest and emit to subscriber
|
|
869
|
-
this.#state.addEffect('set_manifest', ({ payload }) => {
|
|
870
|
-
subscriber.next(payload);
|
|
871
|
-
}),
|
|
872
|
-
);
|
|
873
|
-
|
|
874
|
-
// when stream closes, dispose of subscription to fetch manifest
|
|
875
|
-
subscriber.add(
|
|
876
|
-
// monitor success of fetching manifest and emit to subscriber
|
|
877
|
-
this.#state.addEffect('fetch_manifest::success', ({ payload }) => {
|
|
878
|
-
subscriber.next(payload);
|
|
879
|
-
// application manifest loaded, complete the stream
|
|
880
|
-
subscriber.complete();
|
|
881
|
-
}),
|
|
882
|
-
);
|
|
883
|
-
subscriber.add(
|
|
884
|
-
// monitor failure of fetching manifest and emit error to subscriber
|
|
885
|
-
this.#state.addEffect('fetch_manifest::failure', ({ payload }) => {
|
|
886
|
-
// application manifest failed to load, emit error and complete the stream
|
|
887
|
-
subscriber.error(
|
|
888
|
-
Error('failed to load application manifest', {
|
|
889
|
-
cause: payload,
|
|
890
|
-
}),
|
|
891
|
-
);
|
|
892
|
-
}),
|
|
893
|
-
);
|
|
894
|
-
|
|
895
|
-
// fetch the application manifest
|
|
896
|
-
this.loadManifest();
|
|
897
|
-
});
|
|
898
|
-
}
|
|
899
|
-
|
|
900
|
-
/** @inheritdoc */
|
|
901
|
-
public getManifestAsync(allow_cache = true): Promise<AppManifest> {
|
|
902
|
-
// when allow_cache is true, use first emitted value, otherwise use last emitted value
|
|
903
|
-
const operator = allow_cache ? firstValueFrom : lastValueFrom;
|
|
904
|
-
return operator(this.getManifest(!allow_cache));
|
|
905
|
-
}
|
|
906
|
-
|
|
907
|
-
/** @inheritdoc */
|
|
908
|
-
public getAppModule(force_refresh = false): Observable<AppScriptModule> {
|
|
909
|
-
return new Observable((subscriber) => {
|
|
910
|
-
// emit the cached module immediately, if already loaded
|
|
911
|
-
if (this.#state.value.modules) {
|
|
912
|
-
// emit current value to the subscriber
|
|
913
|
-
subscriber.next(this.#state.value.modules);
|
|
914
|
-
// complete the stream unless a forced refresh was requested
|
|
915
|
-
if (!force_refresh) {
|
|
916
|
-
// complete if no force refresh
|
|
917
|
-
return subscriber.complete();
|
|
918
|
-
}
|
|
919
|
-
}
|
|
920
|
-
|
|
921
|
-
// when stream closes, dispose of subscription to change of state modules
|
|
922
|
-
subscriber.add(
|
|
923
|
-
// monitor changes to state changes of modules and emit to subscriber
|
|
924
|
-
this.#state.addEffect('set_module', ({ payload }) => {
|
|
925
|
-
subscriber.next(payload);
|
|
926
|
-
}),
|
|
927
|
-
);
|
|
928
|
-
|
|
929
|
-
// when stream closes, dispose of subscription script load success
|
|
930
|
-
subscriber.add(
|
|
931
|
-
// monitor success of loading application script and emit to subscriber
|
|
932
|
-
this.#state.addEffect('import_app::success', ({ payload }) => {
|
|
933
|
-
subscriber.next(payload);
|
|
934
|
-
// application module loaded, complete the stream
|
|
935
|
-
subscriber.complete();
|
|
936
|
-
}),
|
|
937
|
-
);
|
|
938
|
-
|
|
939
|
-
// when stream closes, dispose of subscription to script load failure
|
|
940
|
-
subscriber.add(
|
|
941
|
-
// monitor failure of loading application script and emit error to subscriber
|
|
942
|
-
this.#state.addEffect('import_app::failure', ({ payload }) => {
|
|
943
|
-
// application module failed to load, emit error and complete the stream
|
|
944
|
-
subscriber.error(
|
|
945
|
-
Error('failed to load application modules from script', {
|
|
946
|
-
cause: payload,
|
|
947
|
-
}),
|
|
948
|
-
);
|
|
949
|
-
}),
|
|
950
|
-
);
|
|
951
|
-
|
|
952
|
-
// when stream closes, dispose of subscription to fetch manifest
|
|
953
|
-
subscriber.add(
|
|
954
|
-
// fetch application latest manifest and request loading of the application script
|
|
955
|
-
this.getManifest().subscribe((manifest) => {
|
|
956
|
-
// only import the app module if the manifest declares an entry point
|
|
957
|
-
if (manifest.build?.entryPoint) {
|
|
958
|
-
// TODO(#5128) - this should come from backend
|
|
959
|
-
const assetPath =
|
|
960
|
-
manifest.build.assetPath ?? [manifest.appKey, manifest.build.version].join('@');
|
|
961
|
-
// dispatch import_app action to load the application script
|
|
962
|
-
this.#state.next(actions.importApp([assetPath, manifest.build.entryPoint].join('/')));
|
|
963
|
-
} else {
|
|
964
|
-
console.error(
|
|
965
|
-
`The ${manifest.appKey} app is missing a entry in the manifest, upload a build for your app before continuing`,
|
|
966
|
-
);
|
|
967
|
-
}
|
|
968
|
-
}),
|
|
969
|
-
);
|
|
970
|
-
});
|
|
971
|
-
}
|
|
972
|
-
|
|
973
|
-
/** @inheritdoc */
|
|
974
|
-
public getAppModuleAsync(allow_cache = true): Promise<AppScriptModule> {
|
|
975
|
-
// when allow_cache is true, use first emitted value, otherwise use last emitted value
|
|
976
|
-
const operator = allow_cache ? firstValueFrom : lastValueFrom;
|
|
977
|
-
return operator(this.getAppModule(!allow_cache));
|
|
978
|
-
}
|
|
979
|
-
|
|
980
|
-
/** Releases resources held by this app instance. */
|
|
981
|
-
public dispose: VoidFunction;
|
|
982
|
-
}
|
|
983
|
-
|
|
984
|
-
export default App;
|