@equinor/fusion-framework-module-bookmark 3.0.6 → 4.0.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 +30 -18
  2. package/README.md +156 -255
  3. package/dist/esm/BookmarkClient.js +10 -0
  4. package/dist/esm/BookmarkClient.js.map +1 -1
  5. package/dist/esm/BookmarkConfigurator.js +8 -1
  6. package/dist/esm/BookmarkConfigurator.js.map +1 -1
  7. package/dist/esm/BookmarkProvider.error.js +17 -1
  8. package/dist/esm/BookmarkProvider.error.js.map +1 -1
  9. package/dist/esm/BookmarkProvider.selectors.js +14 -7
  10. package/dist/esm/BookmarkProvider.selectors.js.map +1 -1
  11. package/dist/esm/BookmarkProvider.store.js +6 -5
  12. package/dist/esm/BookmarkProvider.store.js.map +1 -1
  13. package/dist/esm/bookmark-config.schema.js +13 -0
  14. package/dist/esm/bookmark-config.schema.js.map +1 -1
  15. package/dist/esm/bookmark-module.js +16 -0
  16. package/dist/esm/bookmark-module.js.map +1 -1
  17. package/dist/esm/bookmark.schemas.js +22 -0
  18. package/dist/esm/bookmark.schemas.js.map +1 -1
  19. package/dist/esm/enable-bookmark.js +21 -3
  20. package/dist/esm/enable-bookmark.js.map +1 -1
  21. package/dist/esm/version.js +1 -1
  22. package/dist/tsconfig.tsbuildinfo +1 -1
  23. package/dist/types/BookmarkClient.d.ts +10 -0
  24. package/dist/types/BookmarkConfigurator.d.ts +8 -1
  25. package/dist/types/BookmarkProvider.actions.d.ts +1 -0
  26. package/dist/types/BookmarkProvider.error.d.ts +18 -1
  27. package/dist/types/BookmarkProvider.interface.d.ts +37 -0
  28. package/dist/types/BookmarkProvider.selectors.d.ts +14 -7
  29. package/dist/types/BookmarkProvider.store.d.ts +16 -7
  30. package/dist/types/bookmark-config.schema.d.ts +13 -0
  31. package/dist/types/bookmark-module.d.ts +23 -0
  32. package/dist/types/bookmark.schemas.d.ts +22 -0
  33. package/dist/types/enable-bookmark.d.ts +21 -3
  34. package/dist/types/version.d.ts +1 -1
  35. package/package.json +12 -13
  36. package/src/BookmarkClient.ts +10 -0
  37. package/src/BookmarkConfigurator.ts +8 -1
  38. package/src/BookmarkProvider.actions.ts +1 -0
  39. package/src/BookmarkProvider.error.ts +15 -0
  40. package/src/BookmarkProvider.interface.ts +37 -0
  41. package/src/BookmarkProvider.selectors.ts +14 -7
  42. package/src/BookmarkProvider.store.ts +16 -7
  43. package/src/bookmark-config.schema.ts +13 -0
  44. package/src/bookmark-module.ts +23 -0
  45. package/src/bookmark.schemas.ts +22 -0
  46. package/src/enable-bookmark.ts +21 -3
  47. package/src/version.ts +1 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,35 @@
1
1
  # Change Log
2
2
 
3
+ ## 4.0.0
4
+
5
+ ### Major Changes
6
+
7
+ - abffa53: Major version bump for Fusion Framework React 19 release.
8
+
9
+ All packages are bumped to the next major version as part of the React 19 upgrade. This release drops support for React versions below 18 and includes breaking changes across the framework.
10
+
11
+ **Breaking changes:**
12
+ - Peer dependencies now require React 18 or 19 (`^18.0.0 || ^19.0.0`)
13
+ - React Router upgraded from v6 to v7
14
+ - Navigation module refactored with new history API
15
+ - `renderComponent` and `renderApp` now use `createRoot` API
16
+
17
+ **Migration:**
18
+ - Update your React version to 18.0.0 or higher before upgrading
19
+ - Replace `NavigationProvider.createRouter()` with `@equinor/fusion-framework-react-router`
20
+ - See individual package changelogs for package-specific migration steps
21
+
22
+ ### Patch Changes
23
+
24
+ - Updated dependencies [abffa53]
25
+ - Updated dependencies [abffa53]
26
+ - Updated dependencies [abffa53]
27
+ - Updated dependencies [abffa53]
28
+ - @equinor/fusion-framework-module@6.0.0
29
+ - @equinor/fusion-log@2.0.0
30
+ - @equinor/fusion-observable@9.0.0
31
+ - @equinor/fusion-query@7.0.0
32
+
3
33
  ## 3.0.6
4
34
 
5
35
  ### Patch Changes
@@ -57,7 +87,6 @@
57
87
  ### Patch Changes
58
88
 
59
89
  - [#3428](https://github.com/equinor/fusion-framework/pull/3428) [`1700ca8`](https://github.com/equinor/fusion-framework/commit/1700ca8851fa108e55e9729fd24f595272766e63) Thanks [@dependabot](https://github.com/apps/dependabot)! - Update zod from 4.1.9 to 4.1.11
60
-
61
90
  - **v4.1.10**: Fixed shape caching issue (#5263) improving validation performance for complex schemas
62
91
  - **v4.1.11**: Maintenance release with general improvements
63
92
 
@@ -77,7 +106,6 @@
77
106
  Updated source code to migrate from zod v3 to v4. Updated zod dependency from v3.25.76 to v4.1.8 and modified schema definitions in the bookmark module to use explicit key and value types for records, simplified function schema definitions, and replaced zod-inferred types with explicit TypeScript interfaces.
78
107
 
79
108
  Key changes in source code:
80
-
81
109
  - Fixed record schema definitions to use explicit key and value types (`z.record(z.string(), z.unknown())`)
82
110
  - Simplified function schema definitions by removing complex chaining (`.args()` and `.returns()`)
83
111
  - Replaced zod-inferred types with explicit TypeScript interfaces for better performance
@@ -87,7 +115,6 @@
87
115
  Breaking changes: Record schemas must specify both key and value types explicitly. Function schema definitions now require explicit typing.
88
116
 
89
117
  Links:
90
-
91
118
  - [Zod v4 Migration Guide](https://github.com/colinhacks/zod/releases/tag/v4.0.0)
92
119
  - [Zod v4.1.8 Release Notes](https://github.com/colinhacks/zod/releases/tag/v4.1.8)
93
120
 
@@ -98,19 +125,16 @@
98
125
  - [#3395](https://github.com/equinor/fusion-framework/pull/3395) [`29f6710`](https://github.com/equinor/fusion-framework/commit/29f6710238baf9b29f42394b30cb8b97f25462c3) Thanks [@odinr](https://github.com/odinr)! - Updated immer from 9.0.16 to 10.1.3 across all packages.
99
126
 
100
127
  ### Breaking Changes
101
-
102
128
  - Immer 10.x introduces stricter TypeScript types for draft functions
103
129
  - `ValidRecipeReturnType` type constraints have changed
104
130
  - Promise return types in draft functions are no longer automatically handled
105
131
 
106
132
  ### Fixes Applied
107
-
108
133
  - Updated BookmarkProvider to handle new immer type constraints
109
134
  - Fixed ObservableInput type assignments in mergeScan operations
110
135
  - Removed async/await from immer draft functions to comply with new type requirements
111
136
 
112
137
  ### Links
113
-
114
138
  - [Immer 10.0.0 Release Notes](https://github.com/immerjs/immer/releases/tag/v10.0.0)
115
139
  - [Immer Migration Guide](https://github.com/immerjs/immer/blob/main/MIGRATION.md)
116
140
 
@@ -119,32 +143,27 @@
119
143
  Updated TypeScript type definitions for UUID operations across multiple packages. This major version update provides improved type safety and compatibility with the latest UUID library features.
120
144
 
121
145
  ### Affected Packages
122
-
123
146
  - @equinor/fusion-framework-module-bookmark: @types/uuid ^9.0.8 → ^11.0.0
124
147
  - @equinor/fusion-framework-module-feature-flag: @types/uuid ^10.0.0 → ^11.0.0
125
148
  - @equinor/fusion-observable: @types/uuid ^10.0.0 → ^11.0.0
126
149
  - @equinor/fusion-query: @types/uuid ^10.0.0 → ^11.0.0
127
150
 
128
151
  ### Links
129
-
130
152
  - [@types/uuid on npm](https://www.npmjs.com/package/@types/uuid)
131
153
  - [DefinitelyTyped @types/uuid](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/uuid)
132
154
 
133
155
  - [#3347](https://github.com/equinor/fusion-framework/pull/3347) [`11143fa`](https://github.com/equinor/fusion-framework/commit/11143fa3002fb8a6c095052a04c7e596c56bafa8) Thanks [@dependabot](https://github.com/apps/dependabot)! - chore: bump uuid from 11.0.3 to 13.0.0
134
156
 
135
157
  ### Breaking Changes
136
-
137
158
  - v13.0.0: Make browser exports the default
138
159
  - v12.0.0: Update to TypeScript 5.2, remove CommonJS support, drop Node 16 support
139
160
 
140
161
  ### New Features
141
-
142
162
  - Improved v4() performance
143
163
  - Added Node 24 to CI matrix
144
164
  - Restored node: prefix support
145
165
 
146
166
  ### Links
147
-
148
167
  - [GitHub releases](https://github.com/uuidjs/uuid/releases/tag/v13.0.0)
149
168
  - [npm changelog](https://www.npmjs.com/package/uuid?activeTab=versions)
150
169
 
@@ -159,14 +178,12 @@
159
178
  - [#3075](https://github.com/equinor/fusion-framework/pull/3075) [`8fffbfb`](https://github.com/equinor/fusion-framework/commit/8fffbfb12daa9748bf5290e5084cd4d409aed253) Thanks [@odinr](https://github.com/odinr)! - Refactored the `Bookmark` type to be an intersection of `BookmarkWithoutData` and an optional `payload` property, improving type safety and flexibility. Updated `useBookmarkNavigate` to use proper TypeScript typing for bookmark events.
160
179
 
161
180
  **Module Bookmark Changes:**
162
-
163
181
  - Refactored `Bookmark` type in `packages/modules/bookmark/src/types.ts`
164
182
  - Added export for `BookmarkProviderEvents` type in `packages/modules/bookmark/src/index.ts`
165
183
  - Updated JSDoc comment from `@note` to `@remarks` in `packages/modules/bookmark/src/BookmarkClient.ts`
166
184
  - Reordered tsconfig references (event before services)
167
185
 
168
186
  **React Changes:**
169
-
170
187
  - Updated `packages/react/modules/bookmark/src/portal/useBookmarkNavigate.ts` to use proper TypeScript typing for bookmark provider events
171
188
  - Removed React paths configuration from `packages/react/app/tsconfig.json`
172
189
 
@@ -201,7 +218,6 @@
201
218
  ### Patch Changes
202
219
 
203
220
  - [#3088](https://github.com/equinor/fusion-framework/pull/3088) [`7441b13`](https://github.com/equinor/fusion-framework/commit/7441b13aa50dd7362d1629086a27b6b4e571575d) Thanks [@eikeland](https://github.com/eikeland)! - chore: update package typesVersions
204
-
205
221
  - Updated package.json typesVersions.
206
222
  - Ensures backward compatibility with older node versions.
207
223
  - Ensured consistency with workspace and repository configuration.
@@ -355,7 +371,6 @@
355
371
  The new implementation has better state management and error handling, and should be easier to work with than the v1 implementation.
356
372
 
357
373
  **Highlights:**
358
-
359
374
  - has validation of configuration of the module.
360
375
  - uses the `BaseConfigBuilder` pattern for configuration.
361
376
  - has validation of requests and responses using zod schemas.
@@ -367,12 +382,10 @@
367
382
  - has better logging.
368
383
 
369
384
  **Migration:**
370
-
371
385
  - update config for enabling the module
372
386
  - check all direct access to provider interface if they are still valid
373
387
 
374
388
  **Breaking changes:**
375
-
376
389
  - The provider interface has changed
377
390
  - The client interface has changed
378
391
  - The configuration interface has changed
@@ -426,7 +439,6 @@
426
439
  - [#2320](https://github.com/equinor/fusion-framework/pull/2320) [`1dd85f3`](https://github.com/equinor/fusion-framework/commit/1dd85f3a408a73df556d1812a5f280945cc100ee) Thanks [@odinr](https://github.com/odinr)! - Removed the `removeComments` option from the `tsconfig.base.json` file.
427
440
 
428
441
  Removing the `removeComments` option allows TypeScript to preserve comments in the compiled JavaScript output. This can be beneficial for several reasons:
429
-
430
442
  1. Improved debugging: Preserved comments can help developers understand the code better during debugging sessions.
431
443
  2. Documentation: JSDoc comments and other important code documentation will be retained in the compiled output.
432
444
  3. Source map accuracy: Keeping comments can lead to more accurate source maps, which is crucial for debugging and error tracking.
package/README.md CHANGED
@@ -1,341 +1,242 @@
1
1
  # Bookmark Module
2
2
 
3
- The Bookmark module provides a way to manage bookmarks in the application. It allows users to create, update, and remove bookmarks, as well as manage the current bookmark and the list of bookmarks.
3
+ `@equinor/fusion-framework-module-bookmark` provides bookmark management for Fusion Framework applications. It lets users **create, update, delete, and favourite bookmarks**, track a **current (active) bookmark**, and react to bookmark lifecycle events.
4
4
 
5
- ## Features
5
+ Under the hood the module uses an RxJS-based state store with side-effect flows, an API client backed by `@equinor/fusion-framework-module-services`, and an event system powered by `@equinor/fusion-framework-module-event`.
6
6
 
7
- - **Create, update, and delete bookmarks**
8
- - **Manage current bookmark**
9
- - **Add and remove bookmarks from favorites**
10
- - **Event listeners for bookmark-related events**
7
+ ## Key Concepts
11
8
 
12
- ## Setup
9
+ | Concept | Description |
10
+ | --- | --- |
11
+ | **BookmarkProvider** | Runtime service exposed after the module initialises. All CRUD, favourites, and current-bookmark operations go through this class. |
12
+ | **BookmarkModuleConfigurator** | Builder used during the configure phase to set the source system, filters, resolvers, and custom client. |
13
+ | **Payload Generator** | Callback registered via `addPayloadGenerator` that participates in building the bookmark payload during create and update. The callback receives an Immer draft — mutate in place. |
14
+ | **Source System** | Identifies which system owns the bookmarks (used for filtering and creation). |
15
+ | **Filters** | Optional flags (`application`, `context`) that restrict bookmark queries to the current app and/or context. |
16
+
17
+ ## Installation
13
18
 
14
19
  ```bash
15
- pnpm install @equinor/fusion-framework-module-bookmark
20
+ pnpm add @equinor/fusion-framework-module-bookmark
16
21
  ```
17
22
 
23
+ ## Quick Start
24
+
25
+ The simplest setup — no custom configuration, inherits everything from the parent (e.g. the portal framework):
26
+
18
27
  ```ts
19
- import { enableBookmarkModule } from ' @equinor/fusion-framework-module-bookmark'
28
+ import { enableBookmark } from '@equinor/fusion-framework-module-bookmark';
29
+
20
30
  const configure = (configurator) => {
21
- // simplest setup
22
- enableBookmarkModule(configurator);
23
- }
31
+ enableBookmark(configurator);
32
+ };
24
33
  ```
25
34
 
26
35
  ## Configuration
36
+
37
+ Use the callback form of `enableBookmark` to customise behaviour:
38
+
27
39
  ```ts
28
- /** example of configuration */
29
- enableBookmarkModule(configurator, async(builder) => {
30
- /** Set source system of bookmark, note used for queries and creation */
40
+ import { enableBookmark } from '@equinor/fusion-framework-module-bookmark';
41
+
42
+ const configure = (configurator) => {
43
+ enableBookmark(configurator, (builder) => {
44
+ // Tag bookmarks with their originating system
31
45
  builder.setSourceSystem({
32
- id: 'source-system-id',
33
- name: 'Source System Name',
46
+ identifier: 'my-app-id',
47
+ name: 'My Application',
34
48
  });
35
49
 
36
- /** apply filters for current application */
50
+ // Only return bookmarks for the current application
37
51
  builder.setFilter('application', true);
38
52
 
39
- /** apply filters for current context */
53
+ // Only return bookmarks for the current context
40
54
  builder.setFilter('context', true);
55
+ });
56
+ };
57
+ ```
41
58
 
42
- /** === Advance Configuration === */
43
-
44
- /** override default implementations */
45
- builder.setClient(/** custom client implementation */)
46
- builder.setParent(/** custom parent implementation */)
59
+ ### Advanced Configuration
47
60
 
48
- /** override default resolvers */
49
- builder.setContextResolver(/** custom context resolver implementation */)
50
- builder.setApplicationResolver(/** custom application resolver implementation */)
61
+ ```ts
62
+ enableBookmark(configurator, (builder) => {
63
+ // Provide a custom API client (must implement IBookmarkClient)
64
+ builder.setClient(myCustomClient);
65
+
66
+ // Inherit bookmarks from a different parent provider
67
+ builder.setParent(parentProvider);
68
+
69
+ // Override how the current context is resolved
70
+ builder.setContextResolver(async (init) => {
71
+ return async () => ({ id: 'custom-context-id' });
72
+ });
73
+
74
+ // Override how the current application is resolved
75
+ builder.setApplicationResolver(async (init) => {
76
+ return async () => ({ appKey: 'my-app' });
77
+ });
51
78
  });
52
79
  ```
53
80
 
54
-
55
81
  > [!NOTE]
56
- > When not providing configuration, the configurator will use the parent (ref initiator, example portal framework) configuration, then fallback to default configuration.
57
- > - `SourceSystem`
58
- > - `Filter`
59
- > - `ContextResolver`
60
- > - `ApplicationResolver`
61
- >
82
+ > When no explicit configuration is provided, the configurator resolves values
83
+ > from the parent framework (portal) and falls back to defaults for:
84
+ > - Source system
85
+ > - Filters
86
+ > - Context resolver
87
+ > - Application resolver
62
88
 
63
89
  ## BookmarkProvider
64
90
 
65
- The `BookmarkProvider` class is responsible for managing bookmarks in the application. It provides methods for creating, updating, and removing bookmarks, as well as managing the current bookmark and the list of bookmarks.
66
-
67
- ### Usage
68
-
69
-
70
- #### Creating a Bookmark
91
+ `BookmarkProvider` is the runtime service you interact with after the module is initialised. It exposes both **Observable** and **async/Promise** APIs for every operation.
71
92
 
72
- To create a new bookmark, use the `createBookmark` method:
93
+ ### Creating a Bookmark
73
94
 
74
95
  ```ts
75
- const newBookmark = {
76
- name: 'My Bookmark',
77
- payload: { /* Your bookmark data */ },
78
- };
96
+ bookmarkProvider.createBookmark({
97
+ name: 'My Bookmark',
98
+ payload: { filters: { status: 'active' } },
99
+ }).subscribe({
100
+ next: (bookmark) => console.log('Created:', bookmark.id),
101
+ error: (err) => console.error('Create failed:', err),
102
+ });
79
103
 
80
- bookmarkProvider.createBookmark(newBookmark).subscribe({
81
- next: (bookmark) => {
82
- console.log('Bookmark created:', bookmark);
83
- },
84
- error: (err) => {
85
- console.error('Failed to create bookmark:', err);
86
- },
104
+ // Or with async/await:
105
+ const bookmark = await bookmarkProvider.createBookmarkAsync({
106
+ name: 'My Bookmark',
107
+ payload: { filters: { status: 'active' } },
87
108
  });
88
109
  ```
89
110
 
90
-
91
- #### Updating a Bookmark
92
-
93
- To update an existing bookmark, use the `updateBookmark` method:
111
+ ### Updating a Bookmark
94
112
 
95
113
  ```ts
96
- const bookmarkId = 'bookmark-id';
97
- const bookmarkUpdates = {
98
- name: 'Updated Bookmark Name',
99
- payload: { /* Updated bookmark data */ },
100
- };
101
-
102
- bookmarkProvider.updateBookmark(bookmarkId, bookmarkUpdates).subscribe({
103
- next: (updatedBookmark) => {
104
- console.log('Bookmark updated:', updatedBookmark);
105
- },
106
- error: (err) => {
107
- console.error('Failed to update bookmark:', err);
108
- },
114
+ bookmarkProvider.updateBookmark('bookmark-id', {
115
+ name: 'Updated Name',
116
+ payload: { filters: { status: 'closed' } },
117
+ }).subscribe({
118
+ next: (updated) => console.log('Updated:', updated.id),
119
+ error: (err) => console.error('Update failed:', err),
109
120
  });
110
121
  ```
111
122
 
112
- #### Using payload generator
113
-
114
- Callback function which triggers on creation or update of bookmark. It is used to modify the payload of the bookmark.
115
-
116
- - payload: The payload of the bookmark, which can be modified of other payload generators.
117
- - initial: The initial payload of the bookmark, which is the payload of the bookmark before any payload generators have been applied.
123
+ ### Deleting a Bookmark
118
124
 
119
125
  ```ts
120
- const bookmarkPayloadGenerator = (payload, initial) => {
121
- payload.counter = payload.counter ?? 0 + initial.counter;
122
- };
126
+ bookmarkProvider.deleteBookmark('bookmark-id').subscribe({
127
+ next: () => console.log('Deleted'),
128
+ error: (err) => console.error('Delete failed:', err),
129
+ });
123
130
  ```
124
131
 
125
- #### Removing a Bookmark
132
+ ### Payload Generators
126
133
 
127
- To remove a bookmark, use the `removeBookmark` method:
134
+ Register a callback that runs during create and update to build or transform the bookmark payload. The callback receives an **Immer draft** — mutate it in place:
128
135
 
129
136
  ```ts
130
- const bookmarkId = 'bookmark-id';
131
-
132
- bookmarkProvider.removeBookmark(bookmarkId).subscribe({
133
- next: (result) => {
134
- console.log('Bookmark removed:', result);
135
- },
136
- error: (err) => {
137
- console.error('Failed to remove bookmark:', err);
138
- },
137
+ const unregister = bookmarkProvider.addPayloadGenerator((payload, initial) => {
138
+ // `payload` is an Immer draft — mutate directly
139
+ payload.savedAt = new Date().toISOString();
140
+ payload.counter = (initial?.counter ?? 0) + 1;
139
141
  });
142
+
143
+ // Call unregister() when the component unmounts to remove the generator
140
144
  ```
141
145
 
146
+ `canCreateBookmarks` returns `true` only when at least one payload generator is registered.
142
147
 
143
- #### Managing Favorites
148
+ ### Current Bookmark
144
149
 
145
- To add a bookmark to favorites, use the `addBookmarkToFavorites` method:
150
+ Set, observe, or clear the active bookmark:
146
151
 
147
152
  ```ts
148
- const bookmarkId = 'bookmark-id';
149
-
150
- bookmarkProvider.addBookmarkToFavorites(bookmarkId).subscribe({
151
- next: (bookmark) => {
152
- console.log('Bookmark added to favorites:', bookmark);
153
- },
154
- error: (err) => {
155
- console.error('Failed to add bookmark to favorites:', err);
156
- },
157
- });
158
- ```
153
+ // Set current bookmark by ID (fetches full data automatically)
154
+ bookmarkProvider.setCurrentBookmark('bookmark-id').subscribe();
159
155
 
160
- To remove a bookmark from favorites, use the `removeBookmarkFromFavorites` method:
156
+ // Clear the current bookmark
157
+ bookmarkProvider.setCurrentBookmark(null).subscribe();
161
158
 
162
- ```ts
163
- const bookmarkId = 'bookmark-id';
164
- bookmarkProvider.removeBookmarkFromFavorites(bookmarkId).subscribe({
165
- next: (bookmark) => {
166
- console.log('Bookmark removed from favorites:', bookmark);
167
- },
168
- error: (err) => {
169
- console.error('Failed to remove bookmark from favorites:', err);
170
- },
159
+ // Observe changes
160
+ bookmarkProvider.currentBookmark$.subscribe((bookmark) => {
161
+ console.log('Active bookmark:', bookmark);
171
162
  });
172
163
  ```
173
164
 
174
-
175
- To check if a bookmark is in favorites, use the `isBookmarkInFavorites` method:
165
+ ### Favourites
176
166
 
177
167
  ```ts
178
- const bookmarkId = 'bookmark-id';
179
-
180
- bookmarkProvider.isBookmarkInFavorites(bookmarkId).subscribe({
181
- next: (isFavorite) => {
182
- console.log('Is bookmark in favorites:', isFavorite);
183
- },
184
- error: (err) => {
185
- console.error('Failed to check if bookmark is in favorites:', err);
186
- },
187
- });
188
- ```
168
+ // Add to favourites
169
+ bookmarkProvider.addBookmarkToFavorites('bookmark-id').subscribe();
189
170
 
171
+ // Remove from favourites
172
+ bookmarkProvider.removeBookmarkAsFavorite('bookmark-id').subscribe();
190
173
 
191
- #### Event Listeners
192
-
193
- You can register event listeners for various bookmark-related events:
194
-
195
- ```ts
196
- const removeListener = bookmarkProvider.on('onCurrentBookmarkChanged', (event) => {
197
- console.log('Current bookmark changed:', event.detail);
174
+ // Check favourite status
175
+ bookmarkProvider.isBookmarkInFavorites('bookmark-id').subscribe((isFav) => {
176
+ console.log('Is favourite:', isFav);
198
177
  });
199
-
200
- // To remove the event listener
201
- removeListener();
202
178
  ```
203
179
 
204
- ## Client
180
+ ### Event Listeners
205
181
 
206
- The `BookmarkClient` class is responsible for fetching bookmarks from the source system. It provides methods for fetching bookmarks by ID, fetching bookmarks by query, and fetching the current bookmark. This is the default implementation of the client and created if not overridden in the configuration.
182
+ Subscribe to lifecycle events emitted by the provider:
207
183
 
208
184
  ```ts
209
- /** Example usage with BookmarkClient as stand alone */
210
- import { BookmarkClient } from '@equinor/fusion-bookmark';
211
- import { BookmarksApiClient } from '@equinor/fusion-framework-module-services/bookmarks';
212
- import { HttpClient } from '@equinor/fusion-framework-module-http/client';
213
-
214
- const httpClient = new HttpClient('https://my-fusion-backend-url.com');
215
- const apiClient = new BookmarksApiClient(httpClient, 'json$');
216
- const client = new BookmarkClient(apiClient);
217
-
218
- client.getAllBookmarks().subscribe(bookmarks => {
219
- // do something with bookmarks
185
+ const unsubscribe = bookmarkProvider.on('onCurrentBookmarkChanged', (event) => {
186
+ console.log('Current bookmark changed:', event.detail);
220
187
  });
221
188
 
222
- client.getBookmarkById('bookmark-id').subscribe(bookmark => {
223
- // do something with bookmark
224
- });
189
+ // Call unsubscribe() to remove the listener
225
190
  ```
226
191
 
227
- ### Custom Client
192
+ Available events:
228
193
 
229
- By implementing the `IBookmarkClient` interface, you can create a custom client implementation.
194
+ | Event | When it fires |
195
+ | --- | --- |
196
+ | `onCurrentBookmarkChange` | **Before** the current bookmark changes (cancelable) |
197
+ | `onCurrentBookmarkChanged` | **After** the current bookmark has changed |
198
+ | `onBookmarkCreate` | Before a bookmark is created (cancelable) |
199
+ | `onBookmarkCreated` | After a bookmark has been created |
200
+ | `onBookmarkUpdate` | Before a bookmark is updated (cancelable) |
201
+ | `onBookmarkUpdated` | After a bookmark has been updated |
202
+ | `onBookmarkDelete` | Before a bookmark is deleted (cancelable) |
203
+ | `onBookmarkDeleted` | After a bookmark has been deleted |
204
+ | `onBookmarkFavouriteAdd` | Before a bookmark is added to favourites (cancelable) |
205
+ | `onBookmarkFavouriteAdded` | After a bookmark has been added to favourites |
206
+ | `onBookmarkFavouriteRemove` | Before a bookmark is removed from favourites (cancelable) |
207
+ | `onBookmarkFavouriteRemoved` | After a bookmark has been removed from favourites |
208
+ | `onBookmarkPayloadCreatorAdded` | When a new payload generator is registered |
230
209
 
231
- ```typescript
232
- import { IBookmarkClient } from '@equinor/fusion-bookmark';
210
+ ## Custom Bookmark Client
233
211
 
234
- export class CustomBookmarkClient implements IBookmarkClient {
235
- // Implement the interface methods
236
- }
212
+ To use a different backend, implement the `IBookmarkClient` interface and pass it to the configurator:
237
213
 
238
- const configure = (configurator) => {
239
- enableBookmarkModule(configurator, async(builder) => {
240
- builder.setClient(new CustomBookmarkClient());
241
- });
214
+ ```ts
215
+ import type { IBookmarkClient } from '@equinor/fusion-framework-module-bookmark';
216
+
217
+ const myClient: IBookmarkClient = {
218
+ getAllBookmarks: (filter) => { /* ... */ },
219
+ getBookmarkById: (id) => { /* ... */ },
220
+ getBookmarkData: (id) => { /* ... */ },
221
+ setBookmarkData: (id, data) => { /* ... */ },
222
+ createBookmark: (bookmark) => { /* ... */ },
223
+ updateBookmark: (id, updates) => { /* ... */ },
224
+ deleteBookmark: (id) => { /* ... */ },
225
+ addBookmarkToFavorites: (id) => { /* ... */ },
226
+ removeBookmarkFromFavorites: (id) => { /* ... */ },
227
+ isBookmarkFavorite: (id) => { /* ... */ },
242
228
  };
243
- ```
244
-
245
- ## Tips
246
-
247
- ## Using "shared" creator across multiple components
248
-
249
- The bookmark module is designed to allow multiple subscribers to collectively manage the payload of an bookmark. This is done by using a payload generator that is triggered on creation and update of the bookmark. This allows multiple components to share the same payload and update it in a safe way.
250
-
251
- To solve this complex problem, internally the bookmark provider uses immer to generate a draft of the payload (a reducer function to update the payload, prevent flux and mutation). This allows the payload to be updated in a safe way, without the need to worry about immutability.
252
-
253
- __BAD!__
254
- ```tsx
255
- /**
256
- * In this example, the mutation of the payload will fail since the currentBookmark is immutable.
257
- */
258
- const RootComponent = () => {
259
- // note deprecated useBookmark
260
- const {
261
- addBookmarkCreator,
262
- currentBookmark
263
- } = useBookmark();
264
-
265
- const payloadRef = useRef(currentBookmark?.payload ?? {});
266
229
 
267
- useEffect(() => {
268
- // since the reference to is return within the reducer, the value will be frozen
269
- // this will break, since `payloadRef.current` is supposed to be updated
270
- return addBookmarkCreator(() => payloadRef.current);
271
- }, [bookmarkProvider]);
272
-
273
- // updates the payloadRef when the current bookmark changes
274
- useEffect(() => {
275
- payloadRef.current = currentBookmark?.payload ?? {};
276
- }, [currentBookmark]);
277
-
278
-
279
- return (
280
- <>
281
- <Component1 payloadRef={payloadRef} />
282
- <Component2 payloadRef={payloadRef} />
283
- </>
284
- );
285
- }
286
-
287
- const Component1 = ({ payloadRef }) => {
288
- return <input
289
- value={ payloadRef.current.foo }
290
- onChange={(e) => payloadRef.current.foo = e.target.value }
291
- />
292
- }
293
-
294
- const Component2 = ({ payloadRef }) => { ... }
295
- ```
296
-
297
- this version can be improved by updating the payload generator, but see next example to migrate of deprecated `useBookmark` to `useCurrentBookmark`.
298
-
299
- ```ts
300
- // fix issue with payload generator
301
- useEffect(() => {
302
- return addBookmarkCreator((payload) => {
303
- Object.assign(payload, payloadRef.current);
304
- });
305
- }, [bookmarkProvider]);
230
+ enableBookmark(configurator, (builder) => {
231
+ builder.setClient(myClient);
232
+ });
306
233
  ```
307
234
 
235
+ ## Error Handling
308
236
 
309
- __BETTER!__
310
- ```tsx
311
-
312
- const RootComponent = () => {
313
- return (
314
- <>
315
- <Component1 />
316
- <Component2 />
317
- </>
318
- );
319
- }
320
-
321
- const Component1 = () => {
322
- const ref = useRef(null);
237
+ The module exposes two error classes:
323
238
 
324
- // internally the payload generator will use immer to generate a draft
325
- const payloadGenerator = useCallback((payload) => {
326
- payload.foo = ref.current.value;
327
- }, []);
239
+ - **`BookmarkProviderError`** thrown by `BookmarkProvider` methods when a high-level operation fails (e.g. timeout, cancelled event, resolution failure).
240
+ - **`BookmarkFlowError`** thrown inside internal store flows when an API call fails. Carries a reference to the originating request action.
328
241
 
329
- // use the current bookmark and register the payload generator
330
- const { currentBookmark } = useCurrentBookmark({ payloadGenerator });
331
-
332
- // when the current bookmark changes, update the input value
333
- useEffect(() => {
334
- ref.current.value = currentBookmark.foo;
335
- }, [currentBookmark]);
336
-
337
- return <input ref={ref} />
338
- }
339
-
340
- Component2 = () => { ... }
341
- ```
242
+ Errors are also collected in the store and accessible via `bookmarkProvider.errors$`.