@equinor/fusion-framework-app 13.0.3 → 13.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/README.md +35 -65
- package/dist/esm/__tests__/mock/AppMockConfigurator.test.js +70 -0
- package/dist/esm/__tests__/mock/AppMockConfigurator.test.js.map +1 -0
- package/dist/esm/__tests__/mock/mock-app.test.js +86 -0
- package/dist/esm/__tests__/mock/mock-app.test.js.map +1 -0
- package/dist/esm/__tests__/mock/msal-hoisting.test.js +36 -0
- package/dist/esm/__tests__/mock/msal-hoisting.test.js.map +1 -0
- package/dist/esm/configure-modules.js +2 -42
- package/dist/esm/configure-modules.js.map +1 -1
- package/dist/esm/initialize-app-modules.js +65 -0
- package/dist/esm/initialize-app-modules.js.map +1 -0
- package/dist/esm/mock/AppMockConfigurator.js +183 -0
- package/dist/esm/mock/AppMockConfigurator.js.map +1 -0
- package/dist/esm/mock/enable-app-manifest-mock.js +49 -0
- package/dist/esm/mock/enable-app-manifest-mock.js.map +1 -0
- package/dist/esm/mock/index.js +21 -0
- package/dist/esm/mock/index.js.map +1 -0
- package/dist/esm/mock/mock-app-modules.js +82 -0
- package/dist/esm/mock/mock-app-modules.js.map +1 -0
- package/dist/esm/version.js +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/types/__tests__/mock/AppMockConfigurator.test.d.ts +1 -0
- package/dist/types/__tests__/mock/mock-app.test.d.ts +1 -0
- package/dist/types/__tests__/mock/msal-hoisting.test.d.ts +1 -0
- package/dist/types/initialize-app-modules.d.ts +31 -0
- package/dist/types/mock/AppMockConfigurator.d.ts +142 -0
- package/dist/types/mock/enable-app-manifest-mock.d.ts +33 -0
- package/dist/types/mock/index.d.ts +20 -0
- package/dist/types/mock/mock-app-modules.d.ts +72 -0
- package/dist/types/version.d.ts +1 -1
- package/package.json +25 -13
- package/CHANGELOG.md +0 -1990
- package/src/AppConfigurator.ts +0 -280
- package/src/AppConfiguratorError.ts +0 -34
- package/src/AppModulesConfiguredEvent.ts +0 -43
- package/src/AppModulesInitializedEvent.ts +0 -40
- package/src/__tests__/AppConfigurator.test.ts +0 -49
- package/src/configure-modules.ts +0 -121
- package/src/enable-bookmark.ts +0 -95
- package/src/enable-state.ts +0 -49
- package/src/index.ts +0 -34
- package/src/types.ts +0 -145
- package/src/utils.ts +0 -50
- package/src/version.ts +0 -2
- package/tsconfig.json +0 -42
- package/vitest.config.ts +0 -10
package/src/AppConfigurator.ts
DELETED
|
@@ -1,280 +0,0 @@
|
|
|
1
|
-
import type { FusionModulesInstance } from '@equinor/fusion-framework';
|
|
2
|
-
|
|
3
|
-
import {
|
|
4
|
-
type AnyModule,
|
|
5
|
-
type IModulesConfigurator,
|
|
6
|
-
ModulesConfigurator,
|
|
7
|
-
} from '@equinor/fusion-framework-module';
|
|
8
|
-
|
|
9
|
-
import event from '@equinor/fusion-framework-module-event';
|
|
10
|
-
|
|
11
|
-
import http, {
|
|
12
|
-
configureHttpClient,
|
|
13
|
-
configureHttp,
|
|
14
|
-
type HttpClientOptions,
|
|
15
|
-
} from '@equinor/fusion-framework-module-http';
|
|
16
|
-
|
|
17
|
-
import auth from '@equinor/fusion-framework-module-msal';
|
|
18
|
-
|
|
19
|
-
import type { AppEnv, AppModules } from './types';
|
|
20
|
-
import { AppModulesConfiguredEvent } from './AppModulesConfiguredEvent';
|
|
21
|
-
import { AppConfiguratorError } from './AppConfiguratorError';
|
|
22
|
-
import { deepClone, deepFreeze, type DeepImmutable } from './utils';
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* Type definition for AppConfigurator constructor
|
|
26
|
-
*/
|
|
27
|
-
export type AppConfiguratorConstructor<
|
|
28
|
-
TModules extends readonly AnyModule[] = [],
|
|
29
|
-
TRef extends FusionModulesInstance = FusionModulesInstance,
|
|
30
|
-
TEnv extends AppEnv = AppEnv,
|
|
31
|
-
> = {
|
|
32
|
-
new (env: TEnv, ref?: TRef): IAppConfigurator<TModules, TRef>;
|
|
33
|
-
};
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* Contract for configuring Fusion application modules.
|
|
37
|
-
*
|
|
38
|
-
* `IAppConfigurator` extends the base module configurator with application-specific
|
|
39
|
-
* methods for setting up HTTP clients and integrating with Fusion service discovery.
|
|
40
|
-
* Use this interface when typing configuration callbacks that receive the configurator.
|
|
41
|
-
*
|
|
42
|
-
* @template TModules - Additional application-specific modules to register beyond the defaults.
|
|
43
|
-
* @template TRef - The resolved Fusion modules instance used as a reference during initialization.
|
|
44
|
-
*
|
|
45
|
-
* @example
|
|
46
|
-
* ```ts
|
|
47
|
-
* import type { IAppConfigurator } from '@equinor/fusion-framework-app';
|
|
48
|
-
*
|
|
49
|
-
* const configure = (configurator: IAppConfigurator) => {
|
|
50
|
-
* configurator.configureHttpClient('myApi', {
|
|
51
|
-
* baseUri: 'https://api.example.com',
|
|
52
|
-
* defaultScopes: ['api://client-id/.default'],
|
|
53
|
-
* });
|
|
54
|
-
* };
|
|
55
|
-
* ```
|
|
56
|
-
*/
|
|
57
|
-
export interface IAppConfigurator<
|
|
58
|
-
TModules extends Array<AnyModule> | unknown = unknown,
|
|
59
|
-
TRef extends FusionModulesInstance = FusionModulesInstance,
|
|
60
|
-
> extends IModulesConfigurator<AppModules<TModules>, TRef> {
|
|
61
|
-
readonly manifest: DeepImmutable<AppEnv['manifest']>;
|
|
62
|
-
|
|
63
|
-
/**
|
|
64
|
-
* Configure the HTTP module with custom settings.
|
|
65
|
-
*
|
|
66
|
-
* Delegates to the framework `configureHttp` helper. Use this when you need
|
|
67
|
-
* low-level control over the HTTP module configuration. For most applications,
|
|
68
|
-
* prefer {@link IAppConfigurator.configureHttpClient | configureHttpClient} instead.
|
|
69
|
-
*
|
|
70
|
-
* @param args - Arguments forwarded to the framework `configureHttp` function.
|
|
71
|
-
*/
|
|
72
|
-
configureHttp(...args: Parameters<typeof configureHttp>): void;
|
|
73
|
-
|
|
74
|
-
/**
|
|
75
|
-
* Register a named HTTP client with explicit base URI and authentication scopes.
|
|
76
|
-
*
|
|
77
|
-
* Use this method when the application needs to call a specific API endpoint
|
|
78
|
-
* that is not provided through Fusion service discovery.
|
|
79
|
-
*
|
|
80
|
-
* @param args - Arguments forwarded to the framework `configureHttpClient` function.
|
|
81
|
-
*
|
|
82
|
-
* @example
|
|
83
|
-
* ```ts
|
|
84
|
-
* configurator.configureHttpClient('myClient', {
|
|
85
|
-
* baseUri: 'https://foo.bar',
|
|
86
|
-
* defaultScopes: ['api://client-id/.default'],
|
|
87
|
-
* });
|
|
88
|
-
* ```
|
|
89
|
-
*/
|
|
90
|
-
configureHttpClient(...args: Parameters<typeof configureHttpClient>): void;
|
|
91
|
-
|
|
92
|
-
/**
|
|
93
|
-
* Register a named HTTP client resolved through Fusion service discovery.
|
|
94
|
-
*
|
|
95
|
-
* The `serviceName` is looked up in the portal’s service-discovery registry at
|
|
96
|
-
* initialization time. Base URI and default scopes are resolved automatically.
|
|
97
|
-
* Use this instead of {@link IAppConfigurator.configureHttpClient | configureHttpClient}
|
|
98
|
-
* when the service is registered with the Fusion portal.
|
|
99
|
-
*
|
|
100
|
-
* @param serviceName - Registered name of the service in Fusion service discovery.
|
|
101
|
-
* @param options - Optional HTTP client overrides (headers, interceptors, etc.).
|
|
102
|
-
* `baseUri` and `defaultScopes` are excluded because they are
|
|
103
|
-
* resolved from service discovery.
|
|
104
|
-
*/
|
|
105
|
-
// TODO(#5060): rename
|
|
106
|
-
useFrameworkServiceClient(
|
|
107
|
-
serviceName: string,
|
|
108
|
-
// biome-ignore lint/suspicious/noExplicitAny: `HttpClientOptions<any>` widens to accept options for any request payload type
|
|
109
|
-
options?: Omit<HttpClientOptions<any>, 'baseUri' | 'defaultScopes'>,
|
|
110
|
-
): void;
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
/**
|
|
114
|
-
* Configurator that bootstraps default Fusion application modules and provides
|
|
115
|
-
* helper methods for HTTP client and service-discovery setup.
|
|
116
|
-
*
|
|
117
|
-
* `AppConfigurator` is created internally by {@link configureModules}. It registers
|
|
118
|
-
* the `event`, `http`, and `msal` (auth) modules by default and reads any HTTP
|
|
119
|
-
* endpoints declared in the application's environment config.
|
|
120
|
-
*
|
|
121
|
-
* @template TModules - Additional application-specific modules beyond the defaults.
|
|
122
|
-
* @template TRef - The resolved Fusion modules instance used as an initialization reference.
|
|
123
|
-
* @template TEnv - The application environment descriptor (manifest, config, basename).
|
|
124
|
-
*
|
|
125
|
-
* @example
|
|
126
|
-
* ```ts
|
|
127
|
-
* // Typically used indirectly via configureModules:
|
|
128
|
-
* import { configureModules } from '@equinor/fusion-framework-app';
|
|
129
|
-
*
|
|
130
|
-
* const initialize = configureModules((configurator) => {
|
|
131
|
-
* configurator.useFrameworkServiceClient('my-service');
|
|
132
|
-
* });
|
|
133
|
-
* ```
|
|
134
|
-
*/
|
|
135
|
-
export class AppConfigurator<
|
|
136
|
-
TModules extends Array<AnyModule> | unknown = unknown,
|
|
137
|
-
TRef extends FusionModulesInstance = FusionModulesInstance,
|
|
138
|
-
TEnv extends AppEnv = AppEnv,
|
|
139
|
-
>
|
|
140
|
-
extends ModulesConfigurator<AppModules<TModules>, TRef>
|
|
141
|
-
implements IAppConfigurator<TModules, TRef>
|
|
142
|
-
{
|
|
143
|
-
/**
|
|
144
|
-
* The class name used for event naming. This static property ensures
|
|
145
|
-
* the name is preserved through compilation and minification.
|
|
146
|
-
*/
|
|
147
|
-
static readonly className: string = 'AppConfigurator';
|
|
148
|
-
|
|
149
|
-
#manifest: DeepImmutable<AppEnv['manifest']>;
|
|
150
|
-
|
|
151
|
-
/**
|
|
152
|
-
* Create an application configurator with default modules and environment.
|
|
153
|
-
*
|
|
154
|
-
* Registers the `event`, `http`, and `msal` modules and pre-configures any
|
|
155
|
-
* HTTP clients declared in `env.config.endpoints`.
|
|
156
|
-
*
|
|
157
|
-
* @param env - The application environment containing manifest, config, and optional basename.
|
|
158
|
-
* @param ref - Optional reference to the Fusion modules instance, used for event dispatching.
|
|
159
|
-
*/
|
|
160
|
-
constructor(
|
|
161
|
-
public readonly env: TEnv,
|
|
162
|
-
ref?: TRef,
|
|
163
|
-
) {
|
|
164
|
-
super([event, http, auth]);
|
|
165
|
-
this.#manifest = deepFreeze(deepClone(env.manifest));
|
|
166
|
-
this._configureHttpClientsFromAppConfig();
|
|
167
|
-
|
|
168
|
-
this.onConfigured((configs) => {
|
|
169
|
-
const configuredEvent = new AppModulesConfiguredEvent<TModules>({
|
|
170
|
-
detail: {
|
|
171
|
-
appKey: this.#manifest.appKey,
|
|
172
|
-
configs,
|
|
173
|
-
},
|
|
174
|
-
});
|
|
175
|
-
ref?.event.dispatchEvent(configuredEvent);
|
|
176
|
-
});
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
/**
|
|
180
|
-
* The immutable application manifest.
|
|
181
|
-
*
|
|
182
|
-
* Deeply frozen at construction time to prevent accidental mutations.
|
|
183
|
-
*
|
|
184
|
-
* @returns The deeply immutable application manifest.
|
|
185
|
-
*/
|
|
186
|
-
get manifest(): DeepImmutable<AppEnv['manifest']> {
|
|
187
|
-
return this.#manifest;
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
/**
|
|
191
|
-
* Read HTTP endpoint definitions from the application config and register each
|
|
192
|
-
* one as a named HTTP client.
|
|
193
|
-
*
|
|
194
|
-
* Iterates over `env.config.endpoints` and calls
|
|
195
|
-
* {@link IAppConfigurator.configureHttpClient | configureHttpClient} for each entry.
|
|
196
|
-
*/
|
|
197
|
-
protected _configureHttpClientsFromAppConfig() {
|
|
198
|
-
const { endpoints = {} } = this.env.config ?? {};
|
|
199
|
-
// Register an HTTP client for each endpoint defined in the app configuration.
|
|
200
|
-
for (const [key, { url, scopes }] of Object.entries(endpoints)) {
|
|
201
|
-
this.configureHttpClient(key, {
|
|
202
|
-
baseUri: url,
|
|
203
|
-
defaultScopes: scopes,
|
|
204
|
-
});
|
|
205
|
-
}
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
/** {@inheritDoc IAppConfigurator.configureHttp} */
|
|
209
|
-
public configureHttp(...args: Parameters<typeof configureHttp>): void {
|
|
210
|
-
this.addConfig(configureHttp(...args));
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
/** {@inheritDoc IAppConfigurator.configureHttpClient} */
|
|
214
|
-
public configureHttpClient(...args: Parameters<typeof configureHttpClient>): void {
|
|
215
|
-
this.addConfig(configureHttpClient(...args));
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
/**
|
|
219
|
-
* Register a named HTTP client whose base URI and scopes are resolved via
|
|
220
|
-
* Fusion service discovery.
|
|
221
|
-
*
|
|
222
|
-
* Resolution priority (highest wins):
|
|
223
|
-
* 1. Session overrides (user-specific URL / scopes)
|
|
224
|
-
* 2. Application config (`env.config.endpoints`)
|
|
225
|
-
* 3. Service-discovery registry
|
|
226
|
-
*
|
|
227
|
-
* If a client with the same `serviceName` is already registered (e.g. from
|
|
228
|
-
* app config) and the service has **not** been overridden at session level,
|
|
229
|
-
* a warning is logged and the existing configuration is kept.
|
|
230
|
-
*
|
|
231
|
-
* @param serviceName - Registered name of the service in Fusion service discovery.
|
|
232
|
-
* @param options - Optional HTTP client overrides. `baseUri` and `defaultScopes`
|
|
233
|
-
* are excluded because they come from service discovery.
|
|
234
|
-
* @throws {Error} When the service cannot be resolved from service discovery.
|
|
235
|
-
*
|
|
236
|
-
* @example
|
|
237
|
-
* ```ts
|
|
238
|
-
* configurator.useFrameworkServiceClient('my-backend-service');
|
|
239
|
-
* ```
|
|
240
|
-
*/
|
|
241
|
-
public useFrameworkServiceClient(
|
|
242
|
-
serviceName: string,
|
|
243
|
-
// biome-ignore lint/suspicious/noExplicitAny: `HttpClientOptions<any>` widens to accept options for any request payload type
|
|
244
|
-
options?: Omit<HttpClientOptions<any>, 'baseUri' | 'defaultScopes'>,
|
|
245
|
-
): void {
|
|
246
|
-
this.addConfig({
|
|
247
|
-
module: http,
|
|
248
|
-
configure: async (config, ref) => {
|
|
249
|
-
// Service from serviceDiscovery with potential session override.
|
|
250
|
-
const service = await ref?.serviceDiscovery.resolveService(serviceName);
|
|
251
|
-
// Guard: service must resolve before the HTTP client can be configured.
|
|
252
|
-
if (!service) {
|
|
253
|
-
throw new AppConfiguratorError(
|
|
254
|
-
`Unable to resolve service [${serviceName}] during configuration.`,
|
|
255
|
-
'configuration',
|
|
256
|
-
);
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
// Check if serviceName is already configured (potentially with app-config)
|
|
260
|
-
// If the service is session overridden - we need the configuration to run
|
|
261
|
-
// as normal (the uri already updated).
|
|
262
|
-
if (config.hasClient(serviceName) && !service.overridden) {
|
|
263
|
-
console.warn(
|
|
264
|
-
`${serviceName} is already configured, possibly by app.config.[ENV].ts.
|
|
265
|
-
Overriding configurations may lead to unintended behaviour and should
|
|
266
|
-
be reviewed carefully.`,
|
|
267
|
-
);
|
|
268
|
-
return;
|
|
269
|
-
}
|
|
270
|
-
config.configureClient(serviceName, {
|
|
271
|
-
...options,
|
|
272
|
-
baseUri: service.uri,
|
|
273
|
-
defaultScopes: service.defaultScopes,
|
|
274
|
-
});
|
|
275
|
-
},
|
|
276
|
-
});
|
|
277
|
-
}
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
export default AppConfigurator;
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Custom error class for application configurator errors.
|
|
3
|
-
*
|
|
4
|
-
* Provides error context to help developers debug configuration and initialization issues.
|
|
5
|
-
*
|
|
6
|
-
* @example
|
|
7
|
-
* ```ts
|
|
8
|
-
* try {
|
|
9
|
-
* const modules = await initialize({ fusion, env });
|
|
10
|
-
* } catch (error) {
|
|
11
|
-
* if (error instanceof AppConfiguratorError) {
|
|
12
|
-
* console.log(`Error in ${error.phase}: ${error.message}`);
|
|
13
|
-
* }
|
|
14
|
-
* }
|
|
15
|
-
* ```
|
|
16
|
-
*/
|
|
17
|
-
export class AppConfiguratorError extends Error {
|
|
18
|
-
/**
|
|
19
|
-
* @param message - Human-readable error description
|
|
20
|
-
* @param phase - The phase where the error occurred
|
|
21
|
-
* @param cause - The underlying error that caused this failure
|
|
22
|
-
*/
|
|
23
|
-
constructor(
|
|
24
|
-
message: string,
|
|
25
|
-
public readonly phase: 'configuration' | 'initialization',
|
|
26
|
-
cause?: unknown,
|
|
27
|
-
) {
|
|
28
|
-
super(message);
|
|
29
|
-
this.name = 'AppConfiguratorError';
|
|
30
|
-
this.cause = cause;
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
export default AppConfiguratorError;
|
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
import type { ModulesConfigType, AnyModule } from '@equinor/fusion-framework-module';
|
|
2
|
-
import type { AppModules } from '@equinor/fusion-framework-module-app';
|
|
3
|
-
import { FrameworkEvent, type FrameworkEventInit } from '@equinor/fusion-framework-module-event';
|
|
4
|
-
import type { AppModulesInitializedEvent } from './AppModulesInitializedEvent';
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* Represents the initialization data for an event indicating that application modules have been configured.
|
|
8
|
-
*
|
|
9
|
-
* @template T - Array of additional modules configured in the application.
|
|
10
|
-
* @extends FrameworkEventInit
|
|
11
|
-
* @property {string} appKey - The unique key identifying the application.
|
|
12
|
-
* @property {ModulesConfigType<AppModules<T>>} configs - The configuration objects for the specified application modules.
|
|
13
|
-
*/
|
|
14
|
-
type AppModulesConfiguredEventInit<T extends AnyModule[] | unknown = unknown> = FrameworkEventInit<{
|
|
15
|
-
appKey: string;
|
|
16
|
-
configs: ModulesConfigType<AppModules<T>>;
|
|
17
|
-
}>;
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* Event emitted when application modules have been configured.
|
|
21
|
-
*
|
|
22
|
-
* @template T - An array of additional modules configured in the application.
|
|
23
|
-
* @extends FrameworkEvent<AppModulesConfiguredEventInit<T>>
|
|
24
|
-
*/
|
|
25
|
-
export class AppModulesConfiguredEvent<
|
|
26
|
-
T extends AnyModule[] | unknown = unknown,
|
|
27
|
-
> extends FrameworkEvent<AppModulesConfiguredEventInit<T>> {
|
|
28
|
-
/**
|
|
29
|
-
* Create an event describing configured application modules.
|
|
30
|
-
*
|
|
31
|
-
* @param init - Event initialization data containing the application key and module configs.
|
|
32
|
-
*/
|
|
33
|
-
constructor(init: AppModulesConfiguredEventInit<T>) {
|
|
34
|
-
super('onAppModulesConfigured', init);
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
declare module '@equinor/fusion-framework-module-event' {
|
|
39
|
-
interface FrameworkEventMap {
|
|
40
|
-
onAppModulesConfigured: AppModulesConfiguredEvent;
|
|
41
|
-
onAppModulesInitialized: AppModulesInitializedEvent;
|
|
42
|
-
}
|
|
43
|
-
}
|
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
import type { AnyModule } from '@equinor/fusion-framework-module';
|
|
2
|
-
import type { AppModulesInstance } from '@equinor/fusion-framework-module-app';
|
|
3
|
-
import { FrameworkEvent, type FrameworkEventInit } from '@equinor/fusion-framework-module-event';
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Event initialization type for the "AppModulesInitialized" event.
|
|
7
|
-
*
|
|
8
|
-
* @template T - An array of module types extending `AnyModule`. Defaults to an empty array.
|
|
9
|
-
* @property appKey - The unique key identifying the application.
|
|
10
|
-
* @property modules - The instance containing all initialized application modules.
|
|
11
|
-
*/
|
|
12
|
-
type AppModulesInitializedEventInit<T extends AnyModule[] | unknown = unknown> =
|
|
13
|
-
FrameworkEventInit<{
|
|
14
|
-
appKey: string;
|
|
15
|
-
modules: AppModulesInstance<T>;
|
|
16
|
-
}>;
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* Event triggered when application modules have been initialized.
|
|
20
|
-
*
|
|
21
|
-
* @template T - An array of modules extending `AnyModule`. Defaults to an empty array.
|
|
22
|
-
* @extends FrameworkEvent<AppModulesInitializedEventInit<T>>
|
|
23
|
-
*
|
|
24
|
-
* @example
|
|
25
|
-
* ```typescript
|
|
26
|
-
* const event = new AppModulesInitializedEvent({ modules: [...] });
|
|
27
|
-
* ```
|
|
28
|
-
*/
|
|
29
|
-
export class AppModulesInitializedEvent<
|
|
30
|
-
T extends AnyModule[] | unknown = unknown,
|
|
31
|
-
> extends FrameworkEvent<AppModulesInitializedEventInit<T>> {
|
|
32
|
-
/**
|
|
33
|
-
* Create an event describing initialized application modules.
|
|
34
|
-
*
|
|
35
|
-
* @param init - Event initialization data containing the application key and module instance.
|
|
36
|
-
*/
|
|
37
|
-
constructor(init: AppModulesInitializedEventInit<T>) {
|
|
38
|
-
super('onAppModulesInitialized', init);
|
|
39
|
-
}
|
|
40
|
-
}
|
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, beforeEach } from 'vitest';
|
|
2
|
-
import { firstValueFrom, take } from 'rxjs';
|
|
3
|
-
import { AppConfigurator } from '../AppConfigurator';
|
|
4
|
-
import type { AnyModule } from '@equinor/fusion-framework-module';
|
|
5
|
-
import { SemanticVersion } from '@equinor/fusion-framework-module';
|
|
6
|
-
|
|
7
|
-
describe('AppConfigurator', () => {
|
|
8
|
-
let configurator: AppConfigurator;
|
|
9
|
-
|
|
10
|
-
// Create a mock module for testing
|
|
11
|
-
const createMockModule = (name: string, version = '1.0.0'): AnyModule => ({
|
|
12
|
-
name,
|
|
13
|
-
version: new SemanticVersion(version),
|
|
14
|
-
initialize: () => ({ mockInstance: true }),
|
|
15
|
-
});
|
|
16
|
-
|
|
17
|
-
// Mock environment object
|
|
18
|
-
const mockEnv = {
|
|
19
|
-
manifest: {
|
|
20
|
-
appKey: 'test-app',
|
|
21
|
-
displayName: 'Test App',
|
|
22
|
-
description: 'A test application',
|
|
23
|
-
type: 'standalone' as const,
|
|
24
|
-
build: {
|
|
25
|
-
version: '1.0.0',
|
|
26
|
-
entryPoint: 'index.js',
|
|
27
|
-
},
|
|
28
|
-
},
|
|
29
|
-
};
|
|
30
|
-
|
|
31
|
-
beforeEach(() => {
|
|
32
|
-
configurator = new AppConfigurator(mockEnv);
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
describe('Event Name Prefixing', () => {
|
|
36
|
-
it('should prefix event names with "AppConfigurator::"', async () => {
|
|
37
|
-
// Trigger event by adding a config
|
|
38
|
-
configurator.addConfig({
|
|
39
|
-
module: createMockModule('test', '1.0.0'),
|
|
40
|
-
configure: () => {},
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
// Wait for the first event to be emitted
|
|
44
|
-
const event = await firstValueFrom(configurator.event$.pipe(take(1)));
|
|
45
|
-
|
|
46
|
-
expect(event.name).toMatch(/^AppConfigurator::/);
|
|
47
|
-
});
|
|
48
|
-
});
|
|
49
|
-
});
|
package/src/configure-modules.ts
DELETED
|
@@ -1,121 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @fileoverview Application module configuration factory
|
|
3
|
-
*
|
|
4
|
-
* Provides the core factory function for configuring and initializing
|
|
5
|
-
* application-specific modules in the Fusion framework.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import type { Fusion } from '@equinor/fusion-framework';
|
|
9
|
-
import type { AnyModule } from '@equinor/fusion-framework-module';
|
|
10
|
-
import {
|
|
11
|
-
enableTelemetry,
|
|
12
|
-
type MetadataExtractor,
|
|
13
|
-
} from '@equinor/fusion-framework-module-telemetry';
|
|
14
|
-
|
|
15
|
-
import { AppConfigurator } from './AppConfigurator';
|
|
16
|
-
|
|
17
|
-
import type { AppModulesInstance, AppModuleInitiator, AppEnv } from './types';
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* Create an application module initializer for a Fusion application.
|
|
21
|
-
*
|
|
22
|
-
* `configureModules` is the primary entry point for setting up an application’s
|
|
23
|
-
* module pipeline. It returns an async function that, when called with the Fusion
|
|
24
|
-
* instance and the application environment, will:
|
|
25
|
-
*
|
|
26
|
-
* 1. Create an {@link AppConfigurator} with the provided environment.
|
|
27
|
-
* 2. Wire up telemetry scoped to the application.
|
|
28
|
-
* 3. Invoke the optional user-supplied configuration callback.
|
|
29
|
-
* 4. Initialize all registered modules and dispatch an `onAppModulesLoaded` event.
|
|
30
|
-
*
|
|
31
|
-
* @template TModules - Additional application-specific modules to register.
|
|
32
|
-
* @template TRef - The Fusion instance type, used as a configuration reference.
|
|
33
|
-
* @template TEnv - The application environment descriptor (manifest, config, basename).
|
|
34
|
-
*
|
|
35
|
-
* @param cb - Optional configuration callback invoked before modules are initialized.
|
|
36
|
-
* Use this to register HTTP clients, enable bookmarks, or add custom modules.
|
|
37
|
-
* @returns An async initializer function that accepts `{ fusion, env }` and resolves
|
|
38
|
-
* with the fully initialized application module instance.
|
|
39
|
-
*
|
|
40
|
-
* @example
|
|
41
|
-
* ```ts
|
|
42
|
-
* import { configureModules } from '@equinor/fusion-framework-app';
|
|
43
|
-
*
|
|
44
|
-
* const initialize = configureModules((configurator, { fusion, env }) => {
|
|
45
|
-
* configurator.useFrameworkServiceClient('my-service');
|
|
46
|
-
* });
|
|
47
|
-
*
|
|
48
|
-
* // Later, during app bootstrap:
|
|
49
|
-
* const modules = await initialize({ fusion, env });
|
|
50
|
-
* ```
|
|
51
|
-
*/
|
|
52
|
-
export const configureModules =
|
|
53
|
-
<
|
|
54
|
-
TModules extends Array<AnyModule> | never,
|
|
55
|
-
TRef extends Fusion = Fusion,
|
|
56
|
-
TEnv extends AppEnv = AppEnv,
|
|
57
|
-
>(
|
|
58
|
-
cb?: AppModuleInitiator<TModules, TRef, TEnv>,
|
|
59
|
-
): ((args: { fusion: TRef; env: TEnv }) => Promise<AppModulesInstance<TModules>>) =>
|
|
60
|
-
/**
|
|
61
|
-
* Async initializer that bootstraps application modules.
|
|
62
|
-
*
|
|
63
|
-
* @param args - Object containing the Fusion instance and the application environment.
|
|
64
|
-
* @param args.fusion - The active Fusion framework instance.
|
|
65
|
-
* @param args.env - The application environment with manifest, config, and basename.
|
|
66
|
-
* @returns The fully initialized application module instance.
|
|
67
|
-
*/
|
|
68
|
-
async (args: { fusion: TRef; env: TEnv }): Promise<AppModulesInstance<TModules>> => {
|
|
69
|
-
const { fusion } = args;
|
|
70
|
-
|
|
71
|
-
// Create app configurator
|
|
72
|
-
const configurator = new AppConfigurator<TModules, TRef['modules'], TEnv>(args.env);
|
|
73
|
-
|
|
74
|
-
// Extract telemetry metadata from app manifest for tracking and debugging
|
|
75
|
-
const metadataExtractor: MetadataExtractor = () => {
|
|
76
|
-
return {
|
|
77
|
-
fusion: {
|
|
78
|
-
type: 'app-telemetry',
|
|
79
|
-
app: {
|
|
80
|
-
key: args.env.manifest?.appKey || 'unknown-app',
|
|
81
|
-
version: args.env.manifest?.build?.version || 'unknown-version',
|
|
82
|
-
},
|
|
83
|
-
},
|
|
84
|
-
};
|
|
85
|
-
};
|
|
86
|
-
|
|
87
|
-
// Enable telemetry collection for module configuration events
|
|
88
|
-
// attachConfiguratorEvents automatically prefixes events with configurator class name
|
|
89
|
-
enableTelemetry(configurator, {
|
|
90
|
-
attachConfiguratorEvents: true,
|
|
91
|
-
configure: (builder) => {
|
|
92
|
-
builder.setMetadata(metadataExtractor);
|
|
93
|
-
builder.setParent(fusion.modules.telemetry);
|
|
94
|
-
// Scope telemetry to 'app' level for app-specific event filtering
|
|
95
|
-
builder.setDefaultScope(['app']);
|
|
96
|
-
},
|
|
97
|
-
});
|
|
98
|
-
|
|
99
|
-
// Allow user configuration callback to run before module initialization
|
|
100
|
-
if (cb) {
|
|
101
|
-
await Promise.resolve(cb(configurator, args));
|
|
102
|
-
}
|
|
103
|
-
// Type cast is safe because AppConfigurator.initialize() returns the exact module
|
|
104
|
-
// instance that was registered and configured above. The intermediate 'unknown'
|
|
105
|
-
// cast is necessary due to TypeScript's generic inference limitations with the
|
|
106
|
-
// configurator's initialization chain, but the runtime value is guaranteed to match.
|
|
107
|
-
const modules: AppModulesInstance<TModules> = (await configurator.initialize(
|
|
108
|
-
args.fusion.modules,
|
|
109
|
-
)) as unknown as AppModulesInstance<TModules>;
|
|
110
|
-
|
|
111
|
-
// Dispatch app modules loaded event for app lifecycle tracking
|
|
112
|
-
// TODO(#5061): remove check after fusion-cli is updated (app module is not enabled in fusion-cli)
|
|
113
|
-
if (args.env.manifest?.appKey) {
|
|
114
|
-
modules.event.dispatchEvent('onAppModulesLoaded', {
|
|
115
|
-
detail: { appKey: args.env.manifest.appKey, manifest: args.env.manifest, modules },
|
|
116
|
-
});
|
|
117
|
-
}
|
|
118
|
-
return modules;
|
|
119
|
-
};
|
|
120
|
-
|
|
121
|
-
export default configureModules;
|
package/src/enable-bookmark.ts
DELETED
|
@@ -1,95 +0,0 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
BookmarkModule,
|
|
3
|
-
BookmarkPayloadGenerator,
|
|
4
|
-
} from '@equinor/fusion-framework-module-bookmark';
|
|
5
|
-
import type { IAppConfigurator } from './AppConfigurator';
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* Enable the bookmark module for a Fusion application.
|
|
9
|
-
*
|
|
10
|
-
* Adds bookmark support by wiring the portal’s bookmark provider into the
|
|
11
|
-
* application’s module set. Payload generators registered by the application
|
|
12
|
-
* are automatically cleaned up when the module is disposed, preventing memory
|
|
13
|
-
* leaks across application load/unload cycles.
|
|
14
|
-
*
|
|
15
|
-
* Import this function from `@equinor/fusion-framework-app/enable-bookmark` or, for
|
|
16
|
-
* React apps, from `@equinor/fusion-framework-react-app/bookmark`.
|
|
17
|
-
*
|
|
18
|
-
* @remarks
|
|
19
|
-
* - The portal must expose a bookmark provider on `ref.bookmark`; if it is
|
|
20
|
-
* missing, an error is logged and the module initializes as a no-op.
|
|
21
|
-
* - The `@equinor/fusion-framework-module-bookmark` package must be installed,
|
|
22
|
-
* but do **not** call its `enableBookmark` directly in app code — use this
|
|
23
|
-
* app-level enabler instead.
|
|
24
|
-
*
|
|
25
|
-
* @param config - The application configurator to register the bookmark module on.
|
|
26
|
-
*
|
|
27
|
-
* @example
|
|
28
|
-
* ```ts
|
|
29
|
-
* import { configureModules } from '@equinor/fusion-framework-app';
|
|
30
|
-
* import { enableBookmark } from '@equinor/fusion-framework-app/enable-bookmark';
|
|
31
|
-
*
|
|
32
|
-
* const initialize = configureModules((configurator) => {
|
|
33
|
-
* enableBookmark(configurator);
|
|
34
|
-
* });
|
|
35
|
-
* ```
|
|
36
|
-
*/
|
|
37
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
38
|
-
export const enableBookmark = (config: IAppConfigurator): void => {
|
|
39
|
-
// collection of cleanup functions
|
|
40
|
-
const cleanupFunctions = new Set<VoidFunction>();
|
|
41
|
-
config.addConfig({
|
|
42
|
-
module: {
|
|
43
|
-
name: 'bookmark',
|
|
44
|
-
initialize(args) {
|
|
45
|
-
// get the bookmark provider from the ref (portal)
|
|
46
|
-
const provider = args.ref?.bookmark;
|
|
47
|
-
// Bail out early if the portal did not expose a bookmark provider.
|
|
48
|
-
if (!provider) {
|
|
49
|
-
console.error('Bookmark provider not found');
|
|
50
|
-
return {};
|
|
51
|
-
}
|
|
52
|
-
// create a proxy to intercept the addPayloadGenerator method
|
|
53
|
-
return new Proxy(provider, {
|
|
54
|
-
get(target, prop) {
|
|
55
|
-
// Intercept specific property access to wrap bookmark lifecycle management.
|
|
56
|
-
switch (prop) {
|
|
57
|
-
case 'addPayloadGenerator':
|
|
58
|
-
return (generator: BookmarkPayloadGenerator) => {
|
|
59
|
-
// catch the teardown function and add it to the cleanup functions
|
|
60
|
-
const cleanupHandler = target.addPayloadGenerator(generator);
|
|
61
|
-
cleanupFunctions.add(cleanupHandler);
|
|
62
|
-
// wrap the teardown function to remove it from the cleanup functions
|
|
63
|
-
return () => {
|
|
64
|
-
cleanupFunctions.delete(cleanupHandler);
|
|
65
|
-
cleanupHandler();
|
|
66
|
-
};
|
|
67
|
-
};
|
|
68
|
-
}
|
|
69
|
-
/**
|
|
70
|
-
* If the property is not addPayloadGenerator, we want to access the property on the provider
|
|
71
|
-
*
|
|
72
|
-
* @remarks we can not use the Reflect API to access the property, as the provider is a proxy
|
|
73
|
-
* and the Reflect API will not work as expected (can not access private properties)
|
|
74
|
-
*/
|
|
75
|
-
if (prop in target) {
|
|
76
|
-
// if the property is a function, bind it to the provider
|
|
77
|
-
if (typeof target[prop] === 'function') {
|
|
78
|
-
return target[prop].bind(target);
|
|
79
|
-
}
|
|
80
|
-
// access the property
|
|
81
|
-
return target[prop];
|
|
82
|
-
}
|
|
83
|
-
},
|
|
84
|
-
});
|
|
85
|
-
},
|
|
86
|
-
dispose() {
|
|
87
|
-
// Run all registered cleanup functions to prevent memory leaks on module disposal.
|
|
88
|
-
for (const teardown of cleanupFunctions) {
|
|
89
|
-
teardown();
|
|
90
|
-
}
|
|
91
|
-
cleanupFunctions.clear();
|
|
92
|
-
},
|
|
93
|
-
} satisfies BookmarkModule,
|
|
94
|
-
});
|
|
95
|
-
};
|