Skip to content

Commit 68383ec

Browse files
committed
feat(app): add state management module with PouchDB support and enhance AppConfigurator architecture
1 parent 60dbb86 commit 68383ec

13 files changed

Lines changed: 739 additions & 81 deletions
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
---
2+
"@equinor/fusion-framework-app": minor
3+
---
4+
5+
Add state management module support to @equinor/fusion-framework-app
6+
7+
This change introduces comprehensive state management capabilities to the app package, allowing developers to easily enable persistent state storage in their Fusion applications.
8+
9+
### New Features
10+
11+
- **State Module Enabler**: Added `enableState` function that configures the state module with PouchDB storage
12+
- **Package Exports**: Added new export path `./enable-state` for the state enabler function
13+
- **Type Definitions**: Added typesVersions support for the new state enabler
14+
- **Peer Dependencies**: Added `@equinor/fusion-framework-module-state` as an optional peer dependency
15+
- **Storage Configuration**: Automatic app-scoped storage with key prefixing to prevent state collisions
16+
17+
### Usage
18+
19+
Applications can now enable state management by:
20+
21+
```typescript
22+
import { enableState } from '@equinor/fusion-framework-app/enable-state';
23+
24+
export const configure = (configurator) => {
25+
enableState(configurator);
26+
};
27+
```
28+
29+
The state module provides persistent storage with automatic app-key scoping and uses PouchDB for reliable data persistence across browser sessions.
30+
31+
**Note**: Applications must install `@equinor/fusion-framework-module-state` to use this functionality.
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
---
2+
"@equinor/fusion-framework-app": patch
3+
---
4+
5+
Improve AppConfigurator architecture and error handling
6+
7+
This patch includes several improvements to the AppConfigurator class and overall application module infrastructure for better reliability and developer experience.
8+
9+
**Note**: These changes were originally out of scope for the current state module task, but were included as they had been in TODO status for an extended period and were addressed while making the necessary architectural changes.
10+
11+
### Bug Fixes & Improvements
12+
13+
- **Enhanced Error Handling**: Added `AppConfiguratorError` class with contextual error information
14+
- **Immutable Manifest**: Application manifest is now deeply frozen to prevent accidental mutations
15+
- **Event System**: Added comprehensive event system with `AppModulesConfiguredEvent` and `AppModulesInitializedEvent`
16+
- **Type Safety**: Improved TypeScript definitions with proper generic constraints and constructor types
17+
- **Service Client Configuration**: Enhanced `useFrameworkServiceClient` with better error handling and type safety
18+
- **Utility Functions**: Added deep cloning and freezing utilities for object immutability
19+
- **Documentation**: Extensive JSDoc improvements with examples and detailed parameter descriptions
20+
- **Module Configuration**: Enhanced configurator with better lifecycle management and dependency injection support
21+
22+
### Technical Changes
23+
24+
- Added `lodash.clonedeep` dependency for reliable deep cloning
25+
- Restructured exports for better tree-shaking and module resolution
26+
- Enhanced constructor signature to accept optional framework reference
27+
- Improved module initialization flow with proper event dispatching
28+
- Added comprehensive validation for service discovery operations
29+
30+
These changes provide a more robust foundation for application module configuration while maintaining backward compatibility.

packages/app/README.md

Lines changed: 181 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,196 @@
1-
# @equinor/fusion-framework-app
1+
---
2+
title: "@equinor/fusion-framework-app"
3+
description: "Application foundation package for the Fusion Framework"
4+
category: "Application"
5+
tag:
6+
- app
7+
- core
8+
- foundation
9+
- modules
10+
- framework
11+
- typescript
12+
---
213

3-
> support package for initializing application modules
14+
[<img src="https://img.shields.io/github/package-json/v/equinor/fusion-framework?filename=packages%2Fapp%2Fpackage.json&label=@equinor/fusion-framework-app&style=for-the-badge" />](https://github.com/equinor/fusion-framework/tree/main/packages/app)
415

5-
## 📚 read the [Doc](https://equinor.github.io/fusion-framework/)
16+
## Overview
617

7-
## Bookmarks
18+
The `@equinor/fusion-framework-app` package serves as the foundation for building Fusion applications. It provides a streamlined way to initialize and configure essential modules that most applications need, while keeping the setup lightweight and flexible.
819

9-
The bookmark module provides a way to save and restore the state of the application. This is useful for saving the state of the application when the user navigates away from the application and then returns to the application.
20+
This package acts as a curated collection of core modules with sensible defaults, allowing developers to quickly bootstrap applications without having to manually wire up common functionality like authentication, HTTP communication, and event handling.
1021

11-
> [!IMPORTANT]
12-
> please use the `enableBookmark` from `@equinor/fusion-framework-app/bookmark` or `@equinor/fusion-framework-react-app/bookmark` to enable the bookmark module.
22+
### Key Features
1323

14-
> [!WARNING]
15-
> The application must be install the `@equinor/fusion-framework-module-bookmark` package to use the bookmark module.
16-
>
17-
> __Do not use the `enableBookmark` from that package when developing Fusion Apps__
24+
- **Pre-configured Core Modules**: Essential modules like authentication (MSAL), HTTP client, and event handling are included and configured by default
25+
- **Optional Module System**: Advanced features like state management and bookmarks can be easily enabled when needed
26+
- **Lightweight Design**: Only includes essential dependencies, with optional modules available as peer dependencies
27+
- **Type Safety**: Full TypeScript support with comprehensive type definitions
28+
- **Framework Agnostic**: Works with any JavaScript framework, with specialized React integrations available
29+
30+
### When to Use
31+
32+
Use this package when building:
33+
- Fusion Applications
34+
- Standalone applications that need Fusion Framework capabilities
35+
- Applications requiring authentication, API communication, and event handling
36+
37+
## 📚 Documentation
38+
39+
For comprehensive guides, examples, and API documentation, visit the [Fusion Framework Documentation](https://equinor.github.io/fusion-framework/).
40+
41+
## Modules
42+
43+
The `@equinor/fusion-framework-app` package provides a set of modules that can be used to enhance the functionality of your application. These modules are designed to be used in conjunction with the Fusion Framework and provide a way to integrate various features into your application.
44+
45+
In conjunction, this package offers some module enabler methods for easy integration and configuration of these modules.
46+
47+
### Enabled by default
48+
49+
#### HTTP Module
50+
[<img src="https://img.shields.io/github/package-json/v/equinor/fusion-framework?filename=packages%2Fmodules%2Fhttp%2Fpackage.json&label=@equinor/fusion-framework-module-http&style=for-the-badge" />](https://github.com/equinor/fusion-framework/tree/main/packages/modules/http)
51+
52+
_The HTTP module provides a way to make HTTP requests and handle responses within the application._
53+
54+
> [!TIP]
55+
> If the service is registered in [Service Discovery](#service-discovery) module, you can use the service name to resolve the API endpoint.
56+
57+
```typescript
58+
import type { AppModuleInitiator } from '@equinor/fusion-framework-app';
59+
60+
const configure: AppModuleInitiator = (configurator) => {
61+
configurator.configureHttp((builder) => {
62+
builder.configureHttpClient(
63+
'my-api-client',
64+
{
65+
baseUri: '/api',
66+
defaultScopes: ['api://default']
67+
}
68+
);
69+
});
70+
};
71+
```
72+
73+
#### Auth Module
74+
[<img src="https://img.shields.io/github/package-json/v/equinor/fusion-framework?filename=packages%2Fmodules%2Fmsal%2Fpackage.json&label=@equinor/fusion-framework-module-msal&style=for-the-badge" />](https://github.com/equinor/fusion-framework/tree/main/packages/modules/msal)
75+
76+
_The MSAL module provides a way to integrate Microsoft Authentication Library (MSAL) into the application._
77+
78+
79+
#### Service Discovery
80+
81+
[<img src="https://img.shields.io/github/package-json/v/equinor/fusion-framework?filename=packages%2Fmodules%2Fservice-discovery%2Fpackage.json&label=@equinor/fusion-framework-module-service-discovery&style=for-the-badge" />](https://github.com/equinor/fusion-framework/tree/main/packages/modules/service-discovery)
82+
83+
_The service discovery module provides a way to resolve API endpoints and interact with microservices within the application._
84+
85+
> [!NOTE]
86+
> To resolve a service, it need to be configured which service(s) to use in the application.
87+
88+
```typescript
89+
import type { AppModuleInitiator } from '@equinor/fusion-framework-app';
90+
91+
const configure: AppModuleInitiator = (configurator) => {
92+
configurator.useFrameworkServiceClient('my-registered-service', { /** options */});
93+
};
94+
95+
// example usage after application module initialization
96+
const client = appModules.http.createHttpClient('my-api-client');
97+
```
98+
99+
100+
#### Event Module
101+
[<img src="https://img.shields.io/github/package-json/v/equinor/fusion-framework?filename=packages%2Fmodules%2Fevent%2Fpackage.json&label=@equinor/fusion-framework-module-event&style=for-the-badge" />](https://github.com/equinor/fusion-framework/tree/main/packages/modules/event)
102+
103+
_The event module provides a way to manage and handle events within the application._
104+
105+
### User Enabled
106+
107+
`@equinor/fusion-framework-app` exposes modules for application enablement, since some modules might require application-specific configurations.
108+
109+
> [!NOTE]
110+
> `@equinor/fusion-framework-app` only has these modules as optional dependencies, which means the module need to be installed in the application project to work.
18111
>
19-
> _We might in the future allow application configure their own module, but for now, use the enabler in the app package_
112+
> _We chosen not to include the optional modules to keep the core package lightweight and focused on essential functionality._
113+
114+
> [!WARNING]
115+
> Event though an application may enable modules directly, it is important to ensure that the necessary configurations are in place for each module to function as intended.
20116
21-
### Usage
117+
#### State Management
22118

23-
```ts
24-
import type { AppModuleInitiator, IAppConfigurator } from '@equinor/fusion-framework-react-app';
119+
[<img src="https://img.shields.io/github/package-json/v/equinor/fusion-framework?filename=packages%2Fmodules%2Fstate%2Fpackage.json&label=@equinor/fusion-framework-module-state&style=for-the-badge" />](https://github.com/equinor/fusion-framework/tree/main/packages/modules/state)
120+
121+
_The state management module provides a way to manage the state of the application. This is useful for keeping track of user preferences, application settings, and other data that needs to persist across sessions._
122+
123+
```sh
124+
# Install the state management module
125+
pnpm add @equinor/fusion-framework-module-state
126+
```
127+
128+
```typescript
129+
import type { AppModuleInitiator } from '@equinor/fusion-framework-app';
130+
import { enableState } from '@equinor/fusion-framework-app/state';
131+
132+
const configure: AppModuleInitiator = (configurator) => {
133+
enableState(configurator);
134+
};
135+
```
136+
137+
#### Bookmarks
25138

26-
import { enableBookmark } from '@equinor/fusion-framework-react-app/bookmark';
139+
[<img src="https://img.shields.io/github/package-json/v/equinor/fusion-framework?filename=packages%2Fmodules%2Fbookmark%2Fpackage.json&label=@equinor/fusion-framework-module-bookmark&style=for-the-badge" />](https://github.com/equinor/fusion-framework/tree/main/packages/modules/bookmark)
27140

28-
export const configure: AppModuleInitiator = (configurator: IAppConfigurator) => {
141+
_The bookmark module provides a way to save and restore the state of the application. This is useful for saving the state of the application when the user navigates away from the application and then returns to the application._
142+
143+
```sh
144+
# Install the bookmark module
145+
pnpm add @equinor/fusion-framework-module-bookmark
146+
```
147+
148+
```ts
149+
import type { AppModuleInitiator } from '@equinor/fusion-framework-app';
150+
import { enableBookmark } from '@equinor/fusion-framework-app/bookmark';
151+
152+
const configure: AppModuleInitiator = (configurator) => {
29153
enableBookmark(configurator);
30154
};
31155
```
32156

157+
## Advance
158+
159+
### Extending the Application Configuration
160+
161+
In some case one might want to extend the application configuration to include pre-defined modules and custom behavior,
162+
like maintaining a suite of applications that requires a common set of modules.
163+
164+
> [!NOTE]
165+
> At the moment the utility functions for initializing application modules does not support defining custom application configurations,
166+
> which means that scaffolding needs to be re-implemented.
167+
168+
```ts
169+
import { AppConfigurator, AppEnv } from '@equinor/fusion-framework-app';
170+
171+
type CustomAppManifest = { /** custom attributes */ }
172+
type MyFrameworkModuleInstance = { /** custom module selection */ }
173+
174+
type CustomAppEnv = AppEnv & {
175+
manifest: CustomAppManifest;
176+
/** other custom environment properties */
177+
};
178+
179+
// extending the Fusion AppConfigurator
180+
class ExtendedAppConfigurator<
181+
TModules extends Array<AnyModule> | unknown = unknown,
182+
TRef extends MyFrameworkModuleInstance = MyFrameworkModuleInstance,
183+
TEnv extends CustomAppEnv = CustomAppEnv,
184+
>
185+
extends AppConfigurator<TModules, TRef, TEnv> {
186+
constructor(env: TEnv) {
187+
super(env);
188+
this._modules.push(MyCustomModule);
189+
}
190+
}
191+
```
192+
193+
### Application Configuration with custom framework
194+
195+
`AppConfigurator` in `@equinor/fusion-framework-app` is a superset of `ModulesConfigurator` in `@equinor/fusion-framework-module`,
196+
which means that creating a custom application configuration outside the __Fusion Framework__ is most likely better of with starting from scratch with `ModulesConfigurator`.

packages/app/package.json

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@
99
"types": "./dist/types/index.d.ts",
1010
"import": "./dist/esm/index.js"
1111
},
12+
"./enable-state": {
13+
"types": "./dist/types/enable-state.d.ts",
14+
"import": "./dist/esm/enable-state.js"
15+
},
1216
"./enable-bookmark": {
1317
"types": "./dist/types/enable-bookmark.d.ts",
1418
"import": "./dist/esm/enable-bookmark.js"
@@ -19,6 +23,9 @@
1923
".": [
2024
"dist/types/index.d.ts"
2125
],
26+
"enable-state": [
27+
"dist/types/enable-state.d.ts"
28+
],
2229
"enable-bookmark": [
2330
"dist/types/enable-bookmark.d.ts"
2431
]
@@ -54,18 +61,25 @@
5461
"@equinor/fusion-framework-module-app": "workspace:^",
5562
"@equinor/fusion-framework-module-event": "workspace:^",
5663
"@equinor/fusion-framework-module-http": "workspace:^",
57-
"@equinor/fusion-framework-module-msal": "workspace:^"
64+
"@equinor/fusion-framework-module-msal": "workspace:^",
65+
"lodash.clonedeep": "^4.5.0"
5866
},
5967
"devDependencies": {
6068
"@equinor/fusion-framework-module-bookmark": "workspace:^",
69+
"@equinor/fusion-framework-module-state": "workspace:^",
70+
"@types/lodash.clonedeep": "^4.5.9",
6171
"typescript": "^5.8.2"
6272
},
6373
"peerDependencies": {
64-
"@equinor/fusion-framework-module-bookmark": "workspace:^"
74+
"@equinor/fusion-framework-module-bookmark": "workspace:^",
75+
"@equinor/fusion-framework-module-state": "workspace:^"
6576
},
6677
"peerDependenciesMeta": {
6778
"@equinor/fusion-framework-module-bookmark": {
6879
"optional": true
80+
},
81+
"@equinor/fusion-framework-module-state": {
82+
"optional": true
6983
}
7084
}
7185
}

0 commit comments

Comments
 (0)