@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.
Files changed (46) hide show
  1. package/README.md +35 -65
  2. package/dist/esm/__tests__/mock/AppMockConfigurator.test.js +70 -0
  3. package/dist/esm/__tests__/mock/AppMockConfigurator.test.js.map +1 -0
  4. package/dist/esm/__tests__/mock/mock-app.test.js +86 -0
  5. package/dist/esm/__tests__/mock/mock-app.test.js.map +1 -0
  6. package/dist/esm/__tests__/mock/msal-hoisting.test.js +36 -0
  7. package/dist/esm/__tests__/mock/msal-hoisting.test.js.map +1 -0
  8. package/dist/esm/configure-modules.js +2 -42
  9. package/dist/esm/configure-modules.js.map +1 -1
  10. package/dist/esm/initialize-app-modules.js +65 -0
  11. package/dist/esm/initialize-app-modules.js.map +1 -0
  12. package/dist/esm/mock/AppMockConfigurator.js +183 -0
  13. package/dist/esm/mock/AppMockConfigurator.js.map +1 -0
  14. package/dist/esm/mock/enable-app-manifest-mock.js +49 -0
  15. package/dist/esm/mock/enable-app-manifest-mock.js.map +1 -0
  16. package/dist/esm/mock/index.js +21 -0
  17. package/dist/esm/mock/index.js.map +1 -0
  18. package/dist/esm/mock/mock-app-modules.js +82 -0
  19. package/dist/esm/mock/mock-app-modules.js.map +1 -0
  20. package/dist/esm/version.js +1 -1
  21. package/dist/tsconfig.tsbuildinfo +1 -1
  22. package/dist/types/__tests__/mock/AppMockConfigurator.test.d.ts +1 -0
  23. package/dist/types/__tests__/mock/mock-app.test.d.ts +1 -0
  24. package/dist/types/__tests__/mock/msal-hoisting.test.d.ts +1 -0
  25. package/dist/types/initialize-app-modules.d.ts +31 -0
  26. package/dist/types/mock/AppMockConfigurator.d.ts +142 -0
  27. package/dist/types/mock/enable-app-manifest-mock.d.ts +33 -0
  28. package/dist/types/mock/index.d.ts +20 -0
  29. package/dist/types/mock/mock-app-modules.d.ts +72 -0
  30. package/dist/types/version.d.ts +1 -1
  31. package/package.json +25 -13
  32. package/CHANGELOG.md +0 -1990
  33. package/src/AppConfigurator.ts +0 -280
  34. package/src/AppConfiguratorError.ts +0 -34
  35. package/src/AppModulesConfiguredEvent.ts +0 -43
  36. package/src/AppModulesInitializedEvent.ts +0 -40
  37. package/src/__tests__/AppConfigurator.test.ts +0 -49
  38. package/src/configure-modules.ts +0 -121
  39. package/src/enable-bookmark.ts +0 -95
  40. package/src/enable-state.ts +0 -49
  41. package/src/index.ts +0 -34
  42. package/src/types.ts +0 -145
  43. package/src/utils.ts +0 -50
  44. package/src/version.ts +0 -2
  45. package/tsconfig.json +0 -42
  46. package/vitest.config.ts +0 -10
@@ -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
- });
@@ -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;
@@ -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
- };