@equinor/fusion-framework-app 13.1.0 → 13.1.2
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/version.js +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/types/version.d.ts +1 -1
- package/package.json +15 -12
- package/CHANGELOG.md +0 -2034
- package/docs/bookmarks.md +0 -18
- package/docs/http-clients.md +0 -121
- package/docs/testing.md +0 -105
- 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/__tests__/mock/AppMockConfigurator.test.ts +0 -96
- package/src/__tests__/mock/mock-app.test.ts +0 -112
- package/src/__tests__/mock/msal-hoisting.test.ts +0 -54
- package/src/configure-modules.ts +0 -71
- package/src/enable-bookmark.ts +0 -95
- package/src/enable-state.ts +0 -49
- package/src/index.ts +0 -34
- package/src/initialize-app-modules.ts +0 -98
- package/src/mock/AppMockConfigurator.ts +0 -218
- package/src/mock/enable-app-manifest-mock.ts +0 -62
- package/src/mock/index.ts +0 -21
- package/src/mock/mock-app-modules.ts +0 -109
- 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/docs/bookmarks.md
DELETED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
# Enable Bookmarks
|
|
2
|
-
|
|
3
|
-
The bookmark module allows applications to save and restore application state.
|
|
4
|
-
|
|
5
|
-
> **Important:** Import `enableBookmark` from the app-level package, not from
|
|
6
|
-
> `@equinor/fusion-framework-module-bookmark` directly.
|
|
7
|
-
|
|
8
|
-
```ts
|
|
9
|
-
import { configureModules } from '@equinor/fusion-framework-app';
|
|
10
|
-
import { enableBookmark } from '@equinor/fusion-framework-app/enable-bookmark';
|
|
11
|
-
|
|
12
|
-
const initialize = configureModules((configurator) => {
|
|
13
|
-
enableBookmark(configurator);
|
|
14
|
-
});
|
|
15
|
-
```
|
|
16
|
-
|
|
17
|
-
Payload generators registered through the bookmark module are automatically
|
|
18
|
-
cleaned up when the module is disposed.
|
package/docs/http-clients.md
DELETED
|
@@ -1,121 +0,0 @@
|
|
|
1
|
-
# Configure HTTP Clients
|
|
2
|
-
|
|
3
|
-
The `AppConfigurator` can register named HTTP clients from several sources.
|
|
4
|
-
You retrieve a client at runtime with `framework.modules.http.createClient(name)`.
|
|
5
|
-
|
|
6
|
-
## From Application Config (auto-registration)
|
|
7
|
-
|
|
8
|
-
Endpoints defined in `app.config.<env>.ts` are **automatically registered as
|
|
9
|
-
named HTTP clients** when the `AppConfigurator` is created — no extra code
|
|
10
|
-
needed in `config.ts`.
|
|
11
|
-
|
|
12
|
-
```ts
|
|
13
|
-
// app.config.ts
|
|
14
|
-
import { defineAppConfig } from '@equinor/fusion-framework-cli/app';
|
|
15
|
-
|
|
16
|
-
export default defineAppConfig(() => ({
|
|
17
|
-
endpoints: {
|
|
18
|
-
schedule: {
|
|
19
|
-
url: 'https://schedule-api.example.com',
|
|
20
|
-
scopes: ['api://schedule-id/.default'],
|
|
21
|
-
},
|
|
22
|
-
},
|
|
23
|
-
}));
|
|
24
|
-
```
|
|
25
|
-
|
|
26
|
-
After initialization, use the client directly:
|
|
27
|
-
|
|
28
|
-
```ts
|
|
29
|
-
const client = framework.modules.http.createClient('schedule');
|
|
30
|
-
const data = await client.json('/items');
|
|
31
|
-
```
|
|
32
|
-
|
|
33
|
-
### Mock an application-config endpoint locally
|
|
34
|
-
|
|
35
|
-
Use a direct-only executable service when an app-owned endpoint needs a local OpenAPI mock but
|
|
36
|
-
must not appear in service discovery:
|
|
37
|
-
|
|
38
|
-
```ts
|
|
39
|
-
// mocks/my-api.mock.ts
|
|
40
|
-
import schema from './my-api.openapi.json' with { type: 'json' };
|
|
41
|
-
import { defineService } from '@equinor/fusion-openapi-mock-server/discovery';
|
|
42
|
-
|
|
43
|
-
export default defineService({
|
|
44
|
-
key: 'my-api',
|
|
45
|
-
serviceDiscovery: false,
|
|
46
|
-
schema,
|
|
47
|
-
});
|
|
48
|
-
```
|
|
49
|
-
|
|
50
|
-
Point the development environment at the service's `<key>.localhost` origin:
|
|
51
|
-
|
|
52
|
-
```ts
|
|
53
|
-
// app.config.local.ts
|
|
54
|
-
import { defineAppConfig } from '@equinor/fusion-framework-cli/app';
|
|
55
|
-
|
|
56
|
-
export default defineAppConfig(() => ({
|
|
57
|
-
endpoints: {
|
|
58
|
-
'my-api': {
|
|
59
|
-
url: 'http://my-api.localhost:4010',
|
|
60
|
-
},
|
|
61
|
-
},
|
|
62
|
-
}));
|
|
63
|
-
```
|
|
64
|
-
|
|
65
|
-
Start both foreground processes:
|
|
66
|
-
|
|
67
|
-
```sh
|
|
68
|
-
ffc mock-server
|
|
69
|
-
ffc app dev
|
|
70
|
-
```
|
|
71
|
-
|
|
72
|
-
`serviceDiscovery: false` keeps `my-api` out of discovery while the mock server continues serving
|
|
73
|
-
it directly. The mock server handles browser CORS for these per-service origins, so this workflow
|
|
74
|
-
does not require an `/@fusion-api` proxy route or handwritten `dev-server.config.ts` middleware.
|
|
75
|
-
Restart `ffc app dev` after changing `app.config.<env>.ts`; application config files are resolved at
|
|
76
|
-
startup rather than watched for changes.
|
|
77
|
-
|
|
78
|
-
The `--mock` flag is optional here. It changes where service discovery comes from, but the
|
|
79
|
-
app-owned `endpoints` entry remains explicit in application config. See
|
|
80
|
-
[Develop with mock services](../../dev-server/docs/mocking.md) for discovery modes and isolated
|
|
81
|
-
mock development.
|
|
82
|
-
|
|
83
|
-
## Via Service Discovery
|
|
84
|
-
|
|
85
|
-
```ts
|
|
86
|
-
const initialize = configureModules((configurator) => {
|
|
87
|
-
configurator.useFrameworkServiceClient('people');
|
|
88
|
-
});
|
|
89
|
-
```
|
|
90
|
-
|
|
91
|
-
## Explicit Registration
|
|
92
|
-
|
|
93
|
-
Use `configureHttpClient` in `config.ts` when the endpoint is **not** in
|
|
94
|
-
`app.config.ts`, or when you need custom transport behavior such as headers,
|
|
95
|
-
response guards, or a custom client class.
|
|
96
|
-
|
|
97
|
-
```ts
|
|
98
|
-
configurator.configureHttpClient('custom-api', {
|
|
99
|
-
baseUri: 'https://custom.api.example.com',
|
|
100
|
-
defaultScopes: ['api://custom-id/.default'],
|
|
101
|
-
onCreate: (client) => {
|
|
102
|
-
client.requestHandler.setHeader('X-Source', 'portal');
|
|
103
|
-
},
|
|
104
|
-
});
|
|
105
|
-
```
|
|
106
|
-
|
|
107
|
-
## Resolution Priority
|
|
108
|
-
|
|
109
|
-
When the same client name is configured in more than one place, the
|
|
110
|
-
highest-priority source wins:
|
|
111
|
-
|
|
112
|
-
| Priority | Source | Example |
|
|
113
|
-
|----------|--------|---------|
|
|
114
|
-
| 1 (highest) | **Session overrides** | User-specific URL / scopes set at runtime via `sessionStorage` |
|
|
115
|
-
| 2 | **Application config endpoints** | `endpoints` in `app.config.ts` |
|
|
116
|
-
| 3 | **Service-discovery registry** | Resolved via `useFrameworkServiceClient` |
|
|
117
|
-
| 4 (lowest) | **Explicit registration** | `configureHttpClient(name, options)` in `config.ts` |
|
|
118
|
-
|
|
119
|
-
This means an endpoint defined in `app.config.ts` will override a
|
|
120
|
-
`configureHttpClient` call for the same name, and a session override will
|
|
121
|
-
override both.
|
package/docs/testing.md
DELETED
|
@@ -1,105 +0,0 @@
|
|
|
1
|
-
# Testing
|
|
2
|
-
|
|
3
|
-
`@equinor/fusion-framework-app/mock` runs an application's real module pipeline in
|
|
4
|
-
tests — the real `event`/`http`/`msal` modules, the real `AppConfigurator`
|
|
5
|
-
configuration pipeline, and real lifecycle — while only the boundaries that reach
|
|
6
|
-
outside the process (network access, credentials, a running parent portal) are
|
|
7
|
-
substituted with deterministic fakes. This entry point has no dependency on
|
|
8
|
-
Vitest or any other test runner.
|
|
9
|
-
|
|
10
|
-
```ts
|
|
11
|
-
import { mockAppModules } from '@equinor/fusion-framework-app/mock';
|
|
12
|
-
|
|
13
|
-
const manifest = { appKey: 'my-app', displayName: 'My App', description: 'My app', type: 'standalone' } as const;
|
|
14
|
-
const modules = await mockAppModules(undefined, { manifest });
|
|
15
|
-
```
|
|
16
|
-
|
|
17
|
-
## `mockAppModules(cb, env, fusion?)`
|
|
18
|
-
|
|
19
|
-
Runs the same pipeline `configureModules` produces, against a mocked parent.
|
|
20
|
-
`cb` receives an `AppMockConfigurator` — which *is* an `AppConfigurator`, so
|
|
21
|
-
`useFrameworkServiceClient`, `configureHttpClient`, and any callback written for
|
|
22
|
-
a real app work against it unchanged.
|
|
23
|
-
|
|
24
|
-
- `cb` — configuration callback, or `undefined` to skip it.
|
|
25
|
-
- `env` — the application environment (`manifest`, `config`, `basename`).
|
|
26
|
-
- `fusion` — optional parent Fusion instance. Defaults to a fresh `mockFramework`
|
|
27
|
-
instance with `app` already enabled and this app's own manifest and config
|
|
28
|
-
served (see `enableAppManifestMock` below).
|
|
29
|
-
|
|
30
|
-
```ts
|
|
31
|
-
const manifest = { appKey: 'my-app', displayName: 'My App', description: 'My app', type: 'standalone' } as const;
|
|
32
|
-
|
|
33
|
-
const modules = await mockAppModules(
|
|
34
|
-
(configurator) => {
|
|
35
|
-
configurator.useFrameworkServiceClient('portal-api');
|
|
36
|
-
configurator.http.addMiddleware(async (uri, init, next) =>
|
|
37
|
-
uri === 'https://portal-api.fusion.test/items' ? Response.json([{ id: 1 }]) : next(uri, init),
|
|
38
|
-
);
|
|
39
|
-
},
|
|
40
|
-
{ manifest },
|
|
41
|
-
);
|
|
42
|
-
|
|
43
|
-
const items = await modules.http.createClient('portal-api').json('/items');
|
|
44
|
-
```
|
|
45
|
-
|
|
46
|
-
The default parent's own `app` module (not `mockAppModules`'s returned `modules`)
|
|
47
|
-
only resolves `env.manifest`/`env.config` locally for `env.manifest.appKey` —
|
|
48
|
-
setting the current app to any other key falls through to whatever the parent's
|
|
49
|
-
real `app` module would do (a real service-discovery-resolved request, or
|
|
50
|
-
nothing if `serviceDiscovery` was never pointed anywhere):
|
|
51
|
-
|
|
52
|
-
```ts
|
|
53
|
-
import type { Fusion } from '@equinor/fusion-framework';
|
|
54
|
-
import type { AppModule } from '@equinor/fusion-framework-module-app';
|
|
55
|
-
|
|
56
|
-
await mockAppModules(async (_configurator, { fusion }) => {
|
|
57
|
-
// the default parent always has `app` enabled; cast narrows the module set
|
|
58
|
-
// for callers that pass in a parent without it
|
|
59
|
-
const { app } = (fusion as Fusion<[AppModule]>).modules;
|
|
60
|
-
|
|
61
|
-
app.setCurrentApp(env.manifest.appKey);
|
|
62
|
-
await app.current?.getManifestAsync(); // resolves with env.manifest
|
|
63
|
-
|
|
64
|
-
app.setCurrentApp('some-other-app');
|
|
65
|
-
await app.current?.getManifestAsync(); // rejects — not this app's own manifest
|
|
66
|
-
}, env);
|
|
67
|
-
```
|
|
68
|
-
|
|
69
|
-
## `enableAppManifestMock(configurator, env)`
|
|
70
|
-
|
|
71
|
-
Registers the `app` module on a parent `mockFramework` configurator, serving
|
|
72
|
-
`env.manifest` and `env.config` for this app's own `appKey` while delegating
|
|
73
|
-
every other request — other app keys, tagged requests, builds, settings — to
|
|
74
|
-
whatever client service discovery (or a pre-configured http client) would really
|
|
75
|
-
resolve. `mockAppModules` uses this to build its zero-config default parent;
|
|
76
|
-
call it directly when a test needs to customize `serviceDiscovery` (e.g. point
|
|
77
|
-
it at a real local mock server) while keeping this app's own manifest and
|
|
78
|
-
config servable.
|
|
79
|
-
|
|
80
|
-
```ts
|
|
81
|
-
import { mockFramework } from '@equinor/fusion-framework/mock';
|
|
82
|
-
import type { AppModule } from '@equinor/fusion-framework-module-app';
|
|
83
|
-
import { enableAppManifestMock, mockAppModules } from '@equinor/fusion-framework-app/mock';
|
|
84
|
-
|
|
85
|
-
const env = { manifest: { appKey: 'my-app', displayName: 'My App', description: 'My app', type: 'standalone' } as const };
|
|
86
|
-
|
|
87
|
-
const fusion = await mockFramework<[AppModule]>((configurator) => {
|
|
88
|
-
configurator.serviceDiscovery.setBaseUri('http://localhost:9999');
|
|
89
|
-
enableAppManifestMock(configurator, env);
|
|
90
|
-
});
|
|
91
|
-
|
|
92
|
-
const modules = await mockAppModules(undefined, env, fusion);
|
|
93
|
-
```
|
|
94
|
-
|
|
95
|
-
## `AppMockConfigurator`
|
|
96
|
-
|
|
97
|
-
The configurator type passed to `mockAppModules`'s `cb`. It extends the real
|
|
98
|
-
`AppConfigurator`, so any configuration code written against a real app — named
|
|
99
|
-
HTTP clients, service-discovery clients, bookmark setup — works unchanged
|
|
100
|
-
against it in a test.
|
|
101
|
-
|
|
102
|
-
## Related
|
|
103
|
-
|
|
104
|
-
- [`@equinor/fusion-framework/mock`](../../framework/docs/testing.md) — mock every framework boundary at once, for building a custom parent `fusion` instance.
|
|
105
|
-
- [`@equinor/fusion-framework-module-app/mock`](../../modules/app/README.md) — the lower-level `MockAppClient` this package's mock wiring is built on.
|
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
|
-
}
|