@equinor/fusion-framework-app 13.0.1 → 14.0.0-next.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. package/CHANGELOG.md +70 -0
  2. package/README.md +35 -65
  3. package/dist/esm/__tests__/mock/AppMockConfigurator.test.js +70 -0
  4. package/dist/esm/__tests__/mock/AppMockConfigurator.test.js.map +1 -0
  5. package/dist/esm/__tests__/mock/mock-app.test.js +86 -0
  6. package/dist/esm/__tests__/mock/mock-app.test.js.map +1 -0
  7. package/dist/esm/__tests__/mock/msal-hoisting.test.js +36 -0
  8. package/dist/esm/__tests__/mock/msal-hoisting.test.js.map +1 -0
  9. package/dist/esm/configure-modules.js +2 -42
  10. package/dist/esm/configure-modules.js.map +1 -1
  11. package/dist/esm/initialize-app-modules.js +65 -0
  12. package/dist/esm/initialize-app-modules.js.map +1 -0
  13. package/dist/esm/mock/AppMockConfigurator.js +183 -0
  14. package/dist/esm/mock/AppMockConfigurator.js.map +1 -0
  15. package/dist/esm/mock/enable-app-manifest-mock.js +49 -0
  16. package/dist/esm/mock/enable-app-manifest-mock.js.map +1 -0
  17. package/dist/esm/mock/index.js +21 -0
  18. package/dist/esm/mock/index.js.map +1 -0
  19. package/dist/esm/mock/mock-app-modules.js +82 -0
  20. package/dist/esm/mock/mock-app-modules.js.map +1 -0
  21. package/dist/esm/version.js +1 -1
  22. package/dist/esm/version.js.map +1 -1
  23. package/dist/tsconfig.tsbuildinfo +1 -1
  24. package/dist/types/__tests__/mock/AppMockConfigurator.test.d.ts +1 -0
  25. package/dist/types/__tests__/mock/mock-app.test.d.ts +1 -0
  26. package/dist/types/__tests__/mock/msal-hoisting.test.d.ts +1 -0
  27. package/dist/types/initialize-app-modules.d.ts +31 -0
  28. package/dist/types/mock/AppMockConfigurator.d.ts +142 -0
  29. package/dist/types/mock/enable-app-manifest-mock.d.ts +33 -0
  30. package/dist/types/mock/index.d.ts +20 -0
  31. package/dist/types/mock/mock-app-modules.d.ts +72 -0
  32. package/dist/types/version.d.ts +1 -1
  33. package/docs/bookmarks.md +18 -0
  34. package/docs/http-clients.md +71 -0
  35. package/docs/testing.md +105 -0
  36. package/package.json +22 -13
  37. package/src/__tests__/mock/AppMockConfigurator.test.ts +96 -0
  38. package/src/__tests__/mock/mock-app.test.ts +112 -0
  39. package/src/__tests__/mock/msal-hoisting.test.ts +54 -0
  40. package/src/configure-modules.ts +2 -52
  41. package/src/initialize-app-modules.ts +98 -0
  42. package/src/mock/AppMockConfigurator.ts +218 -0
  43. package/src/mock/enable-app-manifest-mock.ts +62 -0
  44. package/src/mock/index.ts +21 -0
  45. package/src/mock/mock-app-modules.ts +109 -0
  46. package/src/version.ts +1 -1
  47. package/vitest.config.ts +1 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,75 @@
1
1
  # Change Log
2
2
 
3
+ ## 14.0.0-next.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 2836e0b: `enableAppManifestMock` accepts an optional third `assetUri` argument, overriding the base URI a loaded app's script is imported from — useful for tests that dynamically import a real fixture script. `env.config` is now optional; when omitted, a trivial `AppConfig` is used so `App.initialize()` can still resolve.
8
+ - 2836e0b: Add a `./mock` entry point: `mockAppModules` runs an application's real module pipeline — the real `event`/`http`/`msal` modules, the real `AppConfigurator` configuration pipeline and real lifecycle — against a mocked parent Fusion instance, so a test exercises the wiring an application actually depends on instead of a reimplementation of it.
9
+
10
+ ```ts
11
+ import { mockAppModules } from "@equinor/fusion-framework-app/mock";
12
+
13
+ const manifest = {
14
+ appKey: "my-app",
15
+ displayName: "My App",
16
+ description: "My app",
17
+ type: "standalone",
18
+ } as const;
19
+ const modules = await mockAppModules(undefined, { manifest });
20
+ ```
21
+
22
+ `enableAppManifestMock` registers the `app` module on a parent `mockFramework` configurator, serving an app's own manifest and config while delegating every other request to whatever service discovery (or a pre-configured http client) would really resolve. `mockAppModules` uses it to build its zero-configuration default parent; call it directly when a test needs to customize `serviceDiscovery` first.
23
+
24
+ Restructured `README.md` into an entry point pointing at `docs/http-clients.md`, `docs/bookmarks.md` and `docs/testing.md`, matching the convention already used by `@equinor/fusion-framework-module-http` and `@equinor/fusion-framework-module-msal`.
25
+
26
+ ### Patch Changes
27
+
28
+ - e8aae1f: Internal: publish every package on the `next` pre-release tag so the whole framework can be installed as a coherent set.
29
+
30
+ Packages without their own changes are bumped only to receive a `-next.N` version and the `next` dist-tag on npm. Install with:
31
+
32
+ ```bash
33
+ pnpm add @equinor/fusion-framework-react-app@next
34
+ ```
35
+
36
+ - Updated dependencies [e8aae1f]
37
+ - Updated dependencies [2836e0b]
38
+ - Updated dependencies [2836e0b]
39
+ - Updated dependencies [2836e0b]
40
+ - Updated dependencies [2836e0b]
41
+ - Updated dependencies [2836e0b]
42
+ - Updated dependencies [2836e0b]
43
+ - Updated dependencies [2836e0b]
44
+ - Updated dependencies [2836e0b]
45
+ - Updated dependencies [2836e0b]
46
+ - Updated dependencies [2836e0b]
47
+ - Updated dependencies [2836e0b]
48
+ - Updated dependencies [2836e0b]
49
+ - Updated dependencies [2836e0b]
50
+ - Updated dependencies [2836e0b]
51
+ - Updated dependencies [2836e0b]
52
+ - Updated dependencies [2836e0b]
53
+ - Updated dependencies [2836e0b]
54
+ - Updated dependencies [2836e0b]
55
+ - Updated dependencies [2836e0b]
56
+ - Updated dependencies [2836e0b]
57
+ - Updated dependencies [2836e0b]
58
+ - Updated dependencies [2836e0b]
59
+ - Updated dependencies [2836e0b]
60
+ - Updated dependencies [2836e0b]
61
+ - Updated dependencies [2836e0b]
62
+ - Updated dependencies [2836e0b]
63
+ - @equinor/fusion-framework@8.1.0-next.0
64
+ - @equinor/fusion-framework-module@6.1.3-next.0
65
+ - @equinor/fusion-framework-module-app@8.1.0-next.0
66
+ - @equinor/fusion-framework-module-bookmark@4.1.0-next.0
67
+ - @equinor/fusion-framework-module-event@6.1.0-next.0
68
+ - @equinor/fusion-framework-module-http@8.1.0-next.0
69
+ - @equinor/fusion-framework-module-msal@11.0.0-next.0
70
+ - @equinor/fusion-framework-module-state@2.0.1-next.0
71
+ - @equinor/fusion-framework-module-telemetry@8.0.0-next.0
72
+
3
73
  ## 13.0.1
4
74
 
5
75
  ### Patch Changes
package/README.md CHANGED
@@ -20,6 +20,14 @@ single `configureModules` call.
20
20
  > **Most Fusion apps should use `@equinor/fusion-framework-react-app` instead.**
21
21
  > This lower-level package is for framework-agnostic or advanced scenarios.
22
22
 
23
+ ## Documentation
24
+
25
+ | Topic | Description |
26
+ |---|---|
27
+ | [Configure HTTP Clients](docs/http-clients.md) | Named clients from app config, service discovery, and explicit registration, plus resolution priority |
28
+ | [Enable Bookmarks](docs/bookmarks.md) | Registering the bookmark module via `enableBookmark` |
29
+ | [Testing](docs/testing.md) | The `/mock` entry point: `mockAppModules`, `AppMockConfigurator`, and `enableAppManifestMock` |
30
+
23
31
  ## Installation
24
32
 
25
33
  ```sh
@@ -80,6 +88,7 @@ const modules = await initialize({ fusion, env });
80
88
  | `AppModuleInitiator` | Callback signature accepted by `configureModules` for user-supplied setup. |
81
89
  | `AppEnv` | Environment descriptor containing the app manifest, config, and optional basename. |
82
90
  | `enableBookmark` | Helper to enable the bookmark module (import from `@equinor/fusion-framework-app/enable-bookmark`). |
91
+ | `mockAppModules` | Runs the real module pipeline against deterministic fakes for tests (import from `@equinor/fusion-framework-app/mock`). |
83
92
 
84
93
  ## API Surface
85
94
 
@@ -100,40 +109,13 @@ giving you access to:
100
109
  |---|---|
101
110
  | `@equinor/fusion-framework-app` | `configureModules`, `AppConfigurator`, `IAppConfigurator`, all type aliases |
102
111
  | `@equinor/fusion-framework-app/enable-bookmark` | `enableBookmark` function |
112
+ | `@equinor/fusion-framework-app/mock` | `mockAppModules`, `AppMockConfigurator`, `enableAppManifestMock` |
103
113
 
104
114
  ## Configure HTTP Clients
105
115
 
106
- The `AppConfigurator` can register named HTTP clients from several sources.
107
- You retrieve a client at runtime with `framework.modules.http.createClient(name)`.
108
-
109
- ### From Application Config (auto-registration)
110
-
111
- Endpoints defined in `app.config.<env>.ts` are **automatically registered as
112
- named HTTP clients** when the `AppConfigurator` is created — no extra code
113
- needed in `config.ts`.
114
-
115
- ```ts
116
- // app.config.ts
117
- import { defineAppConfig } from '@equinor/fusion-framework-cli/app';
118
-
119
- export default defineAppConfig(() => ({
120
- endpoints: {
121
- schedule: {
122
- url: 'https://schedule-api.example.com',
123
- scopes: ['api://schedule-id/.default'],
124
- },
125
- },
126
- }));
127
- ```
128
-
129
- After initialization, use the client directly:
130
-
131
- ```ts
132
- const client = framework.modules.http.createClient('schedule');
133
- const data = await client.json('/items');
134
- ```
135
-
136
- ### Via Service Discovery
116
+ The `AppConfigurator` can register named HTTP clients from several sources
117
+ application config endpoints, service discovery, or explicit registration —
118
+ and you retrieve one at runtime with `framework.modules.http.createClient(name)`.
137
119
 
138
120
  ```ts
139
121
  const initialize = configureModules((configurator) => {
@@ -141,37 +123,9 @@ const initialize = configureModules((configurator) => {
141
123
  });
142
124
  ```
143
125
 
144
- ### Explicit Registration
145
-
146
- Use `configureHttpClient` in `config.ts` when the endpoint is **not** in
147
- `app.config.ts`, or when you need custom transport behavior such as headers,
148
- response guards, or a custom client class.
149
-
150
- ```ts
151
- configurator.configureHttpClient('custom-api', {
152
- baseUri: 'https://custom.api.example.com',
153
- defaultScopes: ['api://custom-id/.default'],
154
- onCreate: (client) => {
155
- client.requestHandler.setHeader('X-Source', 'portal');
156
- },
157
- });
158
- ```
159
-
160
- ### Resolution Priority
161
-
162
- When the same client name is configured in more than one place, the
163
- highest-priority source wins:
164
-
165
- | Priority | Source | Example |
166
- |----------|--------|---------|
167
- | 1 (highest) | **Session overrides** | User-specific URL / scopes set at runtime via `sessionStorage` |
168
- | 2 | **Application config endpoints** | `endpoints` in `app.config.ts` |
169
- | 3 | **Service-discovery registry** | Resolved via `useFrameworkServiceClient` |
170
- | 4 (lowest) | **Explicit registration** | `configureHttpClient(name, options)` in `config.ts` |
171
-
172
- This means an endpoint defined in `app.config.ts` will override a
173
- `configureHttpClient` call for the same name, and a session override will
174
- override both.
126
+ See [Configure HTTP Clients](docs/http-clients.md) for auto-registration from
127
+ `app.config.ts`, explicit registration, and resolution priority when a client
128
+ is configured in more than one place.
175
129
 
176
130
  ## Enable Bookmarks
177
131
 
@@ -181,7 +135,6 @@ The bookmark module allows applications to save and restore application state.
181
135
  > `@equinor/fusion-framework-module-bookmark` directly.
182
136
 
183
137
  ```ts
184
- import { configureModules } from '@equinor/fusion-framework-app';
185
138
  import { enableBookmark } from '@equinor/fusion-framework-app/enable-bookmark';
186
139
 
187
140
  const initialize = configureModules((configurator) => {
@@ -189,8 +142,25 @@ const initialize = configureModules((configurator) => {
189
142
  });
190
143
  ```
191
144
 
192
- Payload generators registered through the bookmark module are automatically
193
- cleaned up when the module is disposed.
145
+ See [Enable Bookmarks](docs/bookmarks.md) for payload generator cleanup behavior.
146
+
147
+ ## Testing
148
+
149
+ Import from `@equinor/fusion-framework-app/mock` to run an application's real
150
+ module pipeline in tests — the real `event`/`http`/`msal` modules, the real
151
+ `AppConfigurator` configuration pipeline, and real lifecycle — while only the
152
+ boundaries that reach outside the process are substituted with deterministic
153
+ fakes. This entry point has no dependency on Vitest or any other test runner.
154
+
155
+ ```ts
156
+ import { mockAppModules } from '@equinor/fusion-framework-app/mock';
157
+
158
+ const manifest = { appKey: 'my-app', displayName: 'My App', description: 'My app', type: 'standalone' } as const;
159
+ const modules = await mockAppModules(undefined, { manifest });
160
+ ```
161
+
162
+ See [Testing](docs/testing.md) for `AppMockConfigurator`, `enableAppManifestMock`,
163
+ and customizing the mocked parent's service discovery.
194
164
 
195
165
  ## Types
196
166
 
@@ -0,0 +1,70 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { AppConfig } from '@equinor/fusion-framework-module-app';
3
+ import { enableTelemetry } from '@equinor/fusion-framework-module-telemetry';
4
+ import { AppConfigurator } from '../../AppConfigurator.js';
5
+ import { AppMockConfigurator } from '../../mock/AppMockConfigurator.js';
6
+ const mockEnv = {
7
+ manifest: {
8
+ appKey: 'test-app',
9
+ displayName: 'Test App',
10
+ description: 'A test application',
11
+ type: 'standalone',
12
+ build: {
13
+ version: '1.0.0',
14
+ entryPoint: 'index.js',
15
+ },
16
+ },
17
+ };
18
+ describe('AppMockConfigurator', () => {
19
+ it('is a real AppConfigurator', () => {
20
+ const configurator = new AppMockConfigurator(mockEnv);
21
+ expect(configurator).toBeInstanceOf(AppMockConfigurator);
22
+ expect(configurator).toBeInstanceOf(AppConfigurator);
23
+ });
24
+ it('constructs without throwing when env.config declares endpoints', async () => {
25
+ // the base AppConfigurator constructor auto-registers these via addConfig,
26
+ // before this class's own fields (#pinnedModules) are initialized
27
+ const env = {
28
+ ...mockEnv,
29
+ config: new AppConfig({
30
+ endpoints: { status: { url: 'https://status.example.com', scopes: [] } },
31
+ }),
32
+ };
33
+ const configurator = new AppMockConfigurator(env);
34
+ configurator.http.addMiddleware(async (uri, init, next) => uri === 'https://status.example.com/health' ? Response.json({ ok: true }) : next(uri, init));
35
+ enableTelemetry(configurator);
36
+ const modules = await configurator.initialize();
37
+ await expect(modules.http.createClient('status').json('/health')).resolves.toEqual({
38
+ ok: true,
39
+ });
40
+ });
41
+ it('exposes the same http configurator the http module is built from', async () => {
42
+ const configurator = new AppMockConfigurator(mockEnv);
43
+ configurator.http.configureClient('catalog', { baseUri: 'https://api.example.com' });
44
+ configurator.http.addMiddleware(async (uri, init, next) => uri === 'https://api.example.com/items' ? Response.json([{ id: 1 }]) : next(uri, init));
45
+ // msal's config schema requires a telemetry module, normally wired by configureModules
46
+ enableTelemetry(configurator);
47
+ const modules = await configurator.initialize();
48
+ await expect(modules.http.createClient('catalog').json('/items')).resolves.toEqual([{ id: 1 }]);
49
+ });
50
+ it('exposes the same msal configurator the auth module is built from', async () => {
51
+ const configurator = new AppMockConfigurator(mockEnv);
52
+ configurator.msal.setAccount({ name: 'Ada Lovelace' });
53
+ // msal's config schema requires a telemetry module, normally wired by configureModules
54
+ enableTelemetry(configurator);
55
+ const modules = await configurator.initialize();
56
+ expect(modules.auth.account?.name).toBe('Ada Lovelace');
57
+ });
58
+ it('answers a client registered through configureHttpClient via addMiddleware', async () => {
59
+ const configurator = new AppMockConfigurator(mockEnv);
60
+ configurator.configureHttpClient('status-api', { baseUri: 'https://status.example.com' });
61
+ configurator.http.addMiddleware(async (uri, init, next) => uri === 'https://status.example.com/status' ? Response.json({ ok: true }) : next(uri, init));
62
+ // msal's config schema requires a telemetry module, normally wired by configureModules
63
+ enableTelemetry(configurator);
64
+ const modules = await configurator.initialize();
65
+ await expect(modules.http.createClient('status-api').json('/status')).resolves.toEqual({
66
+ ok: true,
67
+ });
68
+ });
69
+ });
70
+ //# sourceMappingURL=AppMockConfigurator.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AppMockConfigurator.test.js","sourceRoot":"","sources":["../../../../src/__tests__/mock/AppMockConfigurator.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,86 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { mockFramework } from '@equinor/fusion-framework/mock';
3
+ import { AppConfig } from '@equinor/fusion-framework-module-app';
4
+ import { AppMockConfigurator } from '../../mock/AppMockConfigurator.js';
5
+ import { mockAppModules } from '../../mock/mock-app-modules.js';
6
+ const env = {
7
+ manifest: {
8
+ appKey: 'test-app',
9
+ displayName: 'Test App',
10
+ description: 'A test application',
11
+ type: 'standalone',
12
+ build: {
13
+ version: '1.0.0',
14
+ entryPoint: 'index.js',
15
+ },
16
+ },
17
+ config: new AppConfig({ environment: { foo: 'bar' } }),
18
+ };
19
+ describe('mockApp', () => {
20
+ it('initializes the app module pipeline with no configuration', async () => {
21
+ const modules = await mockAppModules(undefined, env);
22
+ expect(modules.event).toBeDefined();
23
+ expect(modules.auth).toBeDefined();
24
+ expect(modules.http).toBeDefined();
25
+ });
26
+ it('passes a real AppConfigurator to the callback', async () => {
27
+ expect.assertions(1);
28
+ await mockAppModules((configurator) => {
29
+ expect(configurator).toBeInstanceOf(AppMockConfigurator);
30
+ }, env);
31
+ });
32
+ it('answers a service-discovery-resolved client from the app’s own mocked http module', async () => {
33
+ const modules = await mockAppModules((configurator) => {
34
+ configurator.useFrameworkServiceClient('portal-api');
35
+ // matches against the full resolved URL, so the host is part of the match to keep this
36
+ // from also answering a different client's request
37
+ configurator.http.addMiddleware(async (uri, init, next) => uri === 'https://portal-api.fusion.test/items'
38
+ ? Response.json([{ id: 1 }])
39
+ : next(uri, init));
40
+ }, env);
41
+ await expect(modules.http.createClient('portal-api').json('/items')).resolves.toEqual([
42
+ { id: 1 },
43
+ ]);
44
+ });
45
+ it('reuses an already-mocked fusion instance instead of creating a new one', async () => {
46
+ expect.assertions(1);
47
+ const fusion = await mockFramework((configurator) => {
48
+ configurator.msal.setAccount({ name: 'Ada Lovelace' });
49
+ });
50
+ await mockAppModules((_configurator, { fusion: parent }) => {
51
+ expect(parent.modules.auth.account?.name).toBe('Ada Lovelace');
52
+ }, env, fusion);
53
+ });
54
+ it('awaits an async configure callback before initialize resolves', async () => {
55
+ const modules = await mockAppModules(async (configurator) => {
56
+ await Promise.resolve();
57
+ configurator.msal.setAccount({ name: 'Ada Lovelace' });
58
+ }, env);
59
+ expect(modules.auth.account?.name).toBe('Ada Lovelace');
60
+ });
61
+ it('serves this app’s own manifest and config through the default parent’s app module', async () => {
62
+ expect.assertions(2);
63
+ await mockAppModules(async (_configurator, { fusion }) => {
64
+ // the default parent always has `app` enabled; typed as plain `Fusion` since callers
65
+ // may pass in a parent without it, so the module set is narrowed just for this assertion
66
+ const { app } = fusion.modules;
67
+ app.setCurrentApp(env.manifest.appKey);
68
+ await expect(app.current?.getManifestAsync()).resolves.toMatchObject({
69
+ appKey: env.manifest.appKey,
70
+ displayName: env.manifest.displayName,
71
+ });
72
+ await expect(app.current?.getConfigAsync()).resolves.toMatchObject({
73
+ environment: env.config.environment,
74
+ });
75
+ }, env);
76
+ });
77
+ it('falls through to the real client for a manifest not matching this app’s own', async () => {
78
+ expect.assertions(1);
79
+ await mockAppModules(async (_configurator, { fusion }) => {
80
+ const { app } = fusion.modules;
81
+ app.setCurrentApp('some-other-app');
82
+ await expect(app.current?.getManifestAsync()).rejects.toThrow();
83
+ }, env);
84
+ });
85
+ });
86
+ //# sourceMappingURL=mock-app.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mock-app.test.js","sourceRoot":"","sources":["../../../../src/__tests__/mock/mock-app.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAG9C,OAAO,EAAE,aAAa,EAAE,MAAM,gCAAgC,CAAC;AAC/D,OAAO,EAAE,SAAS,EAAkB,MAAM,sCAAsC,CAAC;AAEjF,OAAO,EAAE,mBAAmB,EAAE,MAAM,mCAAmC,CAAC;AACxE,OAAO,EAAE,cAAc,EAAE,MAAM,gCAAgC,CAAC;AAEhE,MAAM,GAAG,GAAG;IACV,QAAQ,EAAE;QACR,MAAM,EAAE,UAAU;QAClB,WAAW,EAAE,UAAU;QACvB,WAAW,EAAE,oBAAoB;QACjC,IAAI,EAAE,YAAqB;QAC3B,KAAK,EAAE;YACL,OAAO,EAAE,OAAO;YAChB,UAAU,EAAE,UAAU;SACvB;KACF;IACD,MAAM,EAAE,IAAI,SAAS,CAAC,EAAE,WAAW,EAAE,EAAE,GAAG,EAAE,KAAK,EAAE,EAAE,CAAC;CACvD,CAAC;AAEF,QAAQ,CAAC,SAAS,EAAE,GAAG,EAAE;IACvB,EAAE,CAAC,2DAA2D,EAAE,KAAK,IAAI,EAAE;QACzE,MAAM,OAAO,GAAG,MAAM,cAAc,CAAC,SAAS,EAAE,GAAG,CAAC,CAAC;QAErD,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,WAAW,EAAE,CAAC;QACpC,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,WAAW,EAAE,CAAC;QACnC,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,WAAW,EAAE,CAAC;IACrC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,+CAA+C,EAAE,KAAK,IAAI,EAAE;QAC7D,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;QAErB,MAAM,cAAc,CAAC,CAAC,YAAY,EAAE,EAAE;YACpC,MAAM,CAAC,YAAY,CAAC,CAAC,cAAc,CAAC,mBAAmB,CAAC,CAAC;QAC3D,CAAC,EAAE,GAAG,CAAC,CAAC;IACV,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,mFAAmF,EAAE,KAAK,IAAI,EAAE;QACjG,MAAM,OAAO,GAAG,MAAM,cAAc,CAAC,CAAC,YAAY,EAAE,EAAE;YACpD,YAAY,CAAC,yBAAyB,CAAC,YAAY,CAAC,CAAC;YACrD,uFAAuF;YACvF,mDAAmD;YACnD,YAAY,CAAC,IAAI,CAAC,aAAa,CAAC,KAAK,EAAE,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE,CACxD,GAAG,KAAK,sCAAsC;gBAC5C,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,CAAC;gBAC5B,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,CACpB,CAAC;QACJ,CAAC,EAAE,GAAG,CAAC,CAAC;QAER,MAAM,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,YAAY,CAAC,YAAY,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC;YACpF,EAAE,EAAE,EAAE,CAAC,EAAE;SACV,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,wEAAwE,EAAE,KAAK,IAAI,EAAE;QACtF,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;QAErB,MAAM,MAAM,GAAG,MAAM,aAAa,CAAC,CAAC,YAAY,EAAE,EAAE;YAClD,YAAY,CAAC,IAAI,CAAC,UAAU,CAAC,EAAE,IAAI,EAAE,cAAc,EAAE,CAAC,CAAC;QACzD,CAAC,CAAC,CAAC;QAEH,MAAM,cAAc,CAClB,CAAC,aAAa,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE,EAAE;YACpC,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;QACjE,CAAC,EACD,GAAG,EACH,MAAM,CACP,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,+DAA+D,EAAE,KAAK,IAAI,EAAE;QAC7E,MAAM,OAAO,GAAG,MAAM,cAAc,CAAC,KAAK,EAAE,YAAY,EAAE,EAAE;YAC1D,MAAM,OAAO,CAAC,OAAO,EAAE,CAAC;YACxB,YAAY,CAAC,IAAI,CAAC,UAAU,CAAC,EAAE,IAAI,EAAE,cAAc,EAAE,CAAC,CAAC;QACzD,CAAC,EAAE,GAAG,CAAC,CAAC;QAER,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;IAC1D,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,mFAAmF,EAAE,KAAK,IAAI,EAAE;QACjG,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;QAErB,MAAM,cAAc,CAAC,KAAK,EAAE,aAAa,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE;YACvD,qFAAqF;YACrF,yFAAyF;YACzF,MAAM,EAAE,GAAG,EAAE,GAAI,MAA8B,CAAC,OAAO,CAAC;YACxD,GAAG,CAAC,aAAa,CAAC,GAAG,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;YAEvC,MAAM,MAAM,CAAC,GAAG,CAAC,OAAO,EAAE,gBAAgB,EAAE,CAAC,CAAC,QAAQ,CAAC,aAAa,CAAC;gBACnE,MAAM,EAAE,GAAG,CAAC,QAAQ,CAAC,MAAM;gBAC3B,WAAW,EAAE,GAAG,CAAC,QAAQ,CAAC,WAAW;aACtC,CAAC,CAAC;YACH,MAAM,MAAM,CAAC,GAAG,CAAC,OAAO,EAAE,cAAc,EAAE,CAAC,CAAC,QAAQ,CAAC,aAAa,CAAC;gBACjE,WAAW,EAAE,GAAG,CAAC,MAAM,CAAC,WAAW;aACpC,CAAC,CAAC;QACL,CAAC,EAAE,GAAG,CAAC,CAAC;IACV,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,6EAA6E,EAAE,KAAK,IAAI,EAAE;QAC3F,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;QAErB,MAAM,cAAc,CAAC,KAAK,EAAE,aAAa,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE;YACvD,MAAM,EAAE,GAAG,EAAE,GAAI,MAA8B,CAAC,OAAO,CAAC;YACxD,GAAG,CAAC,aAAa,CAAC,gBAAgB,CAAC,CAAC;YAEpC,MAAM,MAAM,CAAC,GAAG,CAAC,OAAO,EAAE,gBAAgB,EAAE,CAAC,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC;QAClE,CAAC,EAAE,GAAG,CAAC,CAAC;IACV,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -0,0 +1,36 @@
1
+ import { describe, expect, it, vi } from 'vitest';
2
+ import { mockFramework } from '@equinor/fusion-framework/mock';
3
+ import { mockAppModules } from '../../mock/mock-app-modules.js';
4
+ const env = {
5
+ manifest: {
6
+ appKey: 'test-app',
7
+ displayName: 'Test App',
8
+ description: 'A test application',
9
+ type: 'standalone',
10
+ },
11
+ };
12
+ describe('msal hoisting', () => {
13
+ it('delegates acquireToken to the parent’s auth module instead of building its own client', async () => {
14
+ const fusion = await mockFramework();
15
+ const result = { accessToken: 'parent-issued-token' };
16
+ vi.spyOn(fusion.modules.auth, 'acquireToken').mockResolvedValue(result);
17
+ const modules = await mockAppModules(undefined, env, fusion);
18
+ // the app's own `auth` is a distinct (proxying) object, not the parent's instance itself
19
+ expect(modules.auth).not.toBe(fusion.modules.auth);
20
+ await expect(modules.auth.acquireToken({ request: { scopes: ['User.Read'] } })).resolves.toMatchObject({ accessToken: 'parent-issued-token' });
21
+ });
22
+ it('surfaces the parent’s acquisition failures instead of falling back to its own client', async () => {
23
+ const fusion = await mockFramework();
24
+ vi.spyOn(fusion.modules.auth, 'acquireToken').mockRejectedValue(new Error('acquisition failed'));
25
+ const modules = await mockAppModules(undefined, env, fusion);
26
+ await expect(modules.auth.acquireToken({ request: { scopes: ['User.Read'] } })).rejects.toThrow('acquisition failed');
27
+ });
28
+ it('reflects the parent’s signed-in account rather than signing in its own', async () => {
29
+ const fusion = await mockFramework((configurator) => {
30
+ configurator.msal.setAccount({ name: 'Ada Lovelace' });
31
+ });
32
+ const modules = await mockAppModules(undefined, env, fusion);
33
+ expect(modules.auth.account?.name).toBe('Ada Lovelace');
34
+ });
35
+ });
36
+ //# sourceMappingURL=msal-hoisting.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"msal-hoisting.test.js","sourceRoot":"","sources":["../../../../src/__tests__/mock/msal-hoisting.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAElD,OAAO,EAAE,aAAa,EAAE,MAAM,gCAAgC,CAAC;AAG/D,OAAO,EAAE,cAAc,EAAE,MAAM,gCAAgC,CAAC;AAEhE,MAAM,GAAG,GAAG;IACV,QAAQ,EAAE;QACR,MAAM,EAAE,UAAU;QAClB,WAAW,EAAE,UAAU;QACvB,WAAW,EAAE,oBAAoB;QACjC,IAAI,EAAE,YAAqB;KAC5B;CACF,CAAC;AAEF,QAAQ,CAAC,eAAe,EAAE,GAAG,EAAE;IAC7B,EAAE,CAAC,uFAAuF,EAAE,KAAK,IAAI,EAAE;QACrG,MAAM,MAAM,GAAG,MAAM,aAAa,EAAE,CAAC;QACrC,MAAM,MAAM,GAAG,EAAE,WAAW,EAAE,qBAAqB,EAA0B,CAAC;QAC9E,EAAE,CAAC,KAAK,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,EAAE,cAAc,CAAC,CAAC,iBAAiB,CAAC,MAAM,CAAC,CAAC;QAExE,MAAM,OAAO,GAAG,MAAM,cAAc,CAAC,SAAS,EAAE,GAAG,EAAE,MAAM,CAAC,CAAC;QAE7D,yFAAyF;QACzF,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QACnD,MAAM,MAAM,CACV,OAAO,CAAC,IAAI,CAAC,YAAY,CAAC,EAAE,OAAO,EAAE,EAAE,MAAM,EAAE,CAAC,WAAW,CAAC,EAAE,EAAE,CAAC,CAClE,CAAC,QAAQ,CAAC,aAAa,CAAC,EAAE,WAAW,EAAE,qBAAqB,EAAE,CAAC,CAAC;IACnE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,sFAAsF,EAAE,KAAK,IAAI,EAAE;QACpG,MAAM,MAAM,GAAG,MAAM,aAAa,EAAE,CAAC;QACrC,EAAE,CAAC,KAAK,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,EAAE,cAAc,CAAC,CAAC,iBAAiB,CAC7D,IAAI,KAAK,CAAC,oBAAoB,CAAC,CAChC,CAAC;QAEF,MAAM,OAAO,GAAG,MAAM,cAAc,CAAC,SAAS,EAAE,GAAG,EAAE,MAAM,CAAC,CAAC;QAE7D,MAAM,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,YAAY,CAAC,EAAE,OAAO,EAAE,EAAE,MAAM,EAAE,CAAC,WAAW,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CAC7F,oBAAoB,CACrB,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,wEAAwE,EAAE,KAAK,IAAI,EAAE;QACtF,MAAM,MAAM,GAAG,MAAM,aAAa,CAAC,CAAC,YAAY,EAAE,EAAE;YAClD,YAAY,CAAC,IAAI,CAAC,UAAU,CAAC,EAAE,IAAI,EAAE,cAAc,EAAE,CAAC,CAAC;QACzD,CAAC,CAAC,CAAC;QAEH,MAAM,OAAO,GAAG,MAAM,cAAc,CAAC,SAAS,EAAE,GAAG,EAAE,MAAM,CAAC,CAAC;QAE7D,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;IAC1D,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -4,8 +4,8 @@
4
4
  * Provides the core factory function for configuring and initializing
5
5
  * application-specific modules in the Fusion framework.
6
6
  */
7
- import { enableTelemetry, } from '@equinor/fusion-framework-module-telemetry';
8
7
  import { AppConfigurator } from './AppConfigurator';
8
+ import { initializeAppModules } from './initialize-app-modules';
9
9
  /**
10
10
  * Create an application module initializer for a Fusion application.
11
11
  *
@@ -49,49 +49,9 @@ export const configureModules = (cb) =>
49
49
  * @returns The fully initialized application module instance.
50
50
  */
51
51
  async (args) => {
52
- const { fusion } = args;
53
52
  // Create app configurator
54
53
  const configurator = new AppConfigurator(args.env);
55
- // Extract telemetry metadata from app manifest for tracking and debugging
56
- const metadataExtractor = () => {
57
- return {
58
- fusion: {
59
- type: 'app-telemetry',
60
- app: {
61
- key: args.env.manifest?.appKey || 'unknown-app',
62
- version: args.env.manifest?.build?.version || 'unknown-version',
63
- },
64
- },
65
- };
66
- };
67
- // Enable telemetry collection for module configuration events
68
- // attachConfiguratorEvents automatically prefixes events with configurator class name
69
- enableTelemetry(configurator, {
70
- attachConfiguratorEvents: true,
71
- configure: (builder) => {
72
- builder.setMetadata(metadataExtractor);
73
- builder.setParent(fusion.modules.telemetry);
74
- // Scope telemetry to 'app' level for app-specific event filtering
75
- builder.setDefaultScope(['app']);
76
- },
77
- });
78
- // Allow user configuration callback to run before module initialization
79
- if (cb) {
80
- await Promise.resolve(cb(configurator, args));
81
- }
82
- // Type cast is safe because AppConfigurator.initialize() returns the exact module
83
- // instance that was registered and configured above. The intermediate 'unknown'
84
- // cast is necessary due to TypeScript's generic inference limitations with the
85
- // configurator's initialization chain, but the runtime value is guaranteed to match.
86
- const modules = (await configurator.initialize(args.fusion.modules));
87
- // Dispatch app modules loaded event for app lifecycle tracking
88
- // TODO(#5061): remove check after fusion-cli is updated (app module is not enabled in fusion-cli)
89
- if (args.env.manifest?.appKey) {
90
- modules.event.dispatchEvent('onAppModulesLoaded', {
91
- detail: { appKey: args.env.manifest.appKey, manifest: args.env.manifest, modules },
92
- });
93
- }
94
- return modules;
54
+ return initializeAppModules(configurator, cb, args);
95
55
  };
96
56
  export default configureModules;
97
57
  //# sourceMappingURL=configure-modules.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"configure-modules.js","sourceRoot":"","sources":["../../src/configure-modules.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,OAAO,EACL,eAAe,GAEhB,MAAM,4CAA4C,CAAC;AAEpD,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAIpD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAC3B,CAKE,EAA6C,EACmC,EAAE;AACpF;;;;;;;GAOG;AACH,KAAK,EAAE,IAAiC,EAAyC,EAAE;IACjF,MAAM,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC;IAExB,0BAA0B;IAC1B,MAAM,YAAY,GAAG,IAAI,eAAe,CAAkC,IAAI,CAAC,GAAG,CAAC,CAAC;IAEpF,0EAA0E;IAC1E,MAAM,iBAAiB,GAAsB,GAAG,EAAE;QAChD,OAAO;YACL,MAAM,EAAE;gBACN,IAAI,EAAE,eAAe;gBACrB,GAAG,EAAE;oBACH,GAAG,EAAE,IAAI,CAAC,GAAG,CAAC,QAAQ,EAAE,MAAM,IAAI,aAAa;oBAC/C,OAAO,EAAE,IAAI,CAAC,GAAG,CAAC,QAAQ,EAAE,KAAK,EAAE,OAAO,IAAI,iBAAiB;iBAChE;aACF;SACF,CAAC;IACJ,CAAC,CAAC;IAEF,8DAA8D;IAC9D,sFAAsF;IACtF,eAAe,CAAC,YAAY,EAAE;QAC5B,wBAAwB,EAAE,IAAI;QAC9B,SAAS,EAAE,CAAC,OAAO,EAAE,EAAE;YACrB,OAAO,CAAC,WAAW,CAAC,iBAAiB,CAAC,CAAC;YACvC,OAAO,CAAC,SAAS,CAAC,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;YAC5C,kEAAkE;YAClE,OAAO,CAAC,eAAe,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC;QACnC,CAAC;KACF,CAAC,CAAC;IAEH,wEAAwE;IACxE,IAAI,EAAE,EAAE,CAAC;QACP,MAAM,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC,YAAY,EAAE,IAAI,CAAC,CAAC,CAAC;IAChD,CAAC;IACD,kFAAkF;IAClF,gFAAgF;IAChF,+EAA+E;IAC/E,qFAAqF;IACrF,MAAM,OAAO,GAAiC,CAAC,MAAM,YAAY,CAAC,UAAU,CAC1E,IAAI,CAAC,MAAM,CAAC,OAAO,CACpB,CAA4C,CAAC;IAE9C,+DAA+D;IAC/D,kGAAkG;IAClG,IAAI,IAAI,CAAC,GAAG,CAAC,QAAQ,EAAE,MAAM,EAAE,CAAC;QAC9B,OAAO,CAAC,KAAK,CAAC,aAAa,CAAC,oBAAoB,EAAE;YAChD,MAAM,EAAE,EAAE,MAAM,EAAE,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,MAAM,EAAE,QAAQ,EAAE,IAAI,CAAC,GAAG,CAAC,QAAQ,EAAE,OAAO,EAAE;SACnF,CAAC,CAAC;IACL,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC,CAAC;AAEJ,eAAe,gBAAgB,CAAC"}
1
+ {"version":3,"file":"configure-modules.js","sourceRoot":"","sources":["../../src/configure-modules.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAKH,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAGpD,OAAO,EAAE,oBAAoB,EAAE,MAAM,0BAA0B,CAAC;AAEhE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAC3B,CAKE,EAA6C,EACmC,EAAE;AACpF;;;;;;;GAOG;AACH,KAAK,EAAE,IAAiC,EAAyC,EAAE;IACjF,0BAA0B;IAC1B,MAAM,YAAY,GAAG,IAAI,eAAe,CAAkC,IAAI,CAAC,GAAG,CAAC,CAAC;IACpF,OAAO,oBAAoB,CAAC,YAAY,EAAE,EAAE,EAAE,IAAI,CAAC,CAAC;AACtD,CAAC,CAAC;AAEJ,eAAe,gBAAgB,CAAC"}
@@ -0,0 +1,65 @@
1
+ import { enableTelemetry, } from '@equinor/fusion-framework-module-telemetry';
2
+ /**
3
+ * Runs the telemetry wiring, the caller's configuration callback and module
4
+ * initialization against an already constructed configurator.
5
+ *
6
+ * @remarks
7
+ * Extracted so `mockAppModules` (`@equinor/fusion-framework-app/mock`) can drive the
8
+ * exact same pipeline against an `AppMockConfigurator` instead of reimplementing
9
+ * it — the same way `FrameworkConfigurator` and `FrameworkMockConfigurator`
10
+ * share the framework's `init`.
11
+ *
12
+ * @param configurator - The (real or mock) app configurator to run the pipeline on.
13
+ * @param cb - Configuration callback invoked before module initialization, or `undefined` to skip it.
14
+ * @param args - Object containing the Fusion instance and the application environment.
15
+ * @returns The fully initialized application module instance.
16
+ * @template TModules - Application module descriptors beyond the default set.
17
+ * @template TRef - The parent Fusion instance type.
18
+ * @template TEnv - The application environment descriptor.
19
+ * @template TConfigurator - The (real or mock) `AppConfigurator` subclass driving the pipeline.
20
+ */
21
+ export async function initializeAppModules(configurator, cb, args) {
22
+ const { fusion } = args;
23
+ // Extract telemetry metadata from app manifest for tracking and debugging
24
+ const metadataExtractor = () => {
25
+ return {
26
+ fusion: {
27
+ type: 'app-telemetry',
28
+ app: {
29
+ key: args.env.manifest?.appKey || 'unknown-app',
30
+ version: args.env.manifest?.build?.version || 'unknown-version',
31
+ },
32
+ },
33
+ };
34
+ };
35
+ // Enable telemetry collection for module configuration events
36
+ // attachConfiguratorEvents automatically prefixes events with configurator class name
37
+ enableTelemetry(configurator, {
38
+ attachConfiguratorEvents: true,
39
+ configure: (builder) => {
40
+ builder.setMetadata(metadataExtractor);
41
+ builder.setParent(fusion.modules.telemetry);
42
+ // Scope telemetry to 'app' level for app-specific event filtering
43
+ builder.setDefaultScope(['app']);
44
+ },
45
+ });
46
+ // Allow user configuration callback to run before module initialization
47
+ if (cb) {
48
+ await Promise.resolve(cb(configurator, args));
49
+ }
50
+ // Type cast is safe because AppConfigurator.initialize() returns the exact module
51
+ // instance that was registered and configured above. The intermediate 'unknown'
52
+ // cast is necessary due to TypeScript's generic inference limitations with the
53
+ // configurator's initialization chain, but the runtime value is guaranteed to match.
54
+ const modules = (await configurator.initialize(args.fusion.modules));
55
+ // Dispatch app modules loaded event for app lifecycle tracking
56
+ // TODO(#5061): remove check after fusion-cli is updated (app module is not enabled in fusion-cli)
57
+ if (args.env.manifest?.appKey) {
58
+ modules.event.dispatchEvent('onAppModulesLoaded', {
59
+ detail: { appKey: args.env.manifest.appKey, manifest: args.env.manifest, modules },
60
+ });
61
+ }
62
+ return modules;
63
+ }
64
+ export default initializeAppModules;
65
+ //# sourceMappingURL=initialize-app-modules.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"initialize-app-modules.js","sourceRoot":"","sources":["../../src/initialize-app-modules.ts"],"names":[],"mappings":"AAEA,OAAO,EACL,eAAe,GAEhB,MAAM,4CAA4C,CAAC;AAKpD;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,CAAC,KAAK,UAAU,oBAAoB,CAaxC,YAA2B,EAC3B,EAEa,EACb,IAAiC;IAEjC,MAAM,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC;IAExB,0EAA0E;IAC1E,MAAM,iBAAiB,GAAsB,GAAG,EAAE;QAChD,OAAO;YACL,MAAM,EAAE;gBACN,IAAI,EAAE,eAAe;gBACrB,GAAG,EAAE;oBACH,GAAG,EAAE,IAAI,CAAC,GAAG,CAAC,QAAQ,EAAE,MAAM,IAAI,aAAa;oBAC/C,OAAO,EAAE,IAAI,CAAC,GAAG,CAAC,QAAQ,EAAE,KAAK,EAAE,OAAO,IAAI,iBAAiB;iBAChE;aACF;SACF,CAAC;IACJ,CAAC,CAAC;IAEF,8DAA8D;IAC9D,sFAAsF;IACtF,eAAe,CAAC,YAAY,EAAE;QAC5B,wBAAwB,EAAE,IAAI;QAC9B,SAAS,EAAE,CAAC,OAAO,EAAE,EAAE;YACrB,OAAO,CAAC,WAAW,CAAC,iBAAiB,CAAC,CAAC;YACvC,OAAO,CAAC,SAAS,CAAC,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;YAC5C,kEAAkE;YAClE,OAAO,CAAC,eAAe,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC;QACnC,CAAC;KACF,CAAC,CAAC;IAEH,wEAAwE;IACxE,IAAI,EAAE,EAAE,CAAC;QACP,MAAM,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC,YAAY,EAAE,IAAI,CAAC,CAAC,CAAC;IAChD,CAAC;IACD,kFAAkF;IAClF,gFAAgF;IAChF,+EAA+E;IAC/E,qFAAqF;IACrF,MAAM,OAAO,GAAiC,CAAC,MAAM,YAAY,CAAC,UAAU,CAC1E,IAAI,CAAC,MAAM,CAAC,OAAO,CACpB,CAA4C,CAAC;IAE9C,+DAA+D;IAC/D,kGAAkG;IAClG,IAAI,IAAI,CAAC,GAAG,CAAC,QAAQ,EAAE,MAAM,EAAE,CAAC;QAC9B,OAAO,CAAC,KAAK,CAAC,aAAa,CAAC,oBAAoB,EAAE;YAChD,MAAM,EAAE,EAAE,MAAM,EAAE,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,MAAM,EAAE,QAAQ,EAAE,IAAI,CAAC,GAAG,CAAC,QAAQ,EAAE,OAAO,EAAE;SACnF,CAAC,CAAC;IACL,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,eAAe,oBAAoB,CAAC"}