feat(state): enable application state management - #3286
Merged
Conversation
🦋 Changeset detectedLatest commit: bd4498e The changes in this PR will be included in the next version bump. This PR includes changesets to release 23 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
Noggling
reviewed
Aug 26, 2025
odinr
force-pushed
the
feat/state/add-state-module
branch
3 times, most recently
from
August 29, 2025 06:44
30516d1 to
722aef3
Compare
odinr
force-pushed
the
feat/state/add-state-module
branch
from
August 29, 2025 06:48
722aef3 to
286494f
Compare
odinr
force-pushed
the
feat/state/add-state-module
branch
5 times, most recently
from
September 1, 2025 11:37
9608c15 to
caaa767
Compare
odinr
force-pushed
the
feat/state/add-state-module
branch
from
September 29, 2025 13:43
4d0e955 to
e68c6d8
Compare
alftore
force-pushed
the
feat/state/add-state-module
branch
from
June 25, 2026 12:56
e68c6d8 to
b5097de
Compare
alftore
added a commit
that referenced
this pull request
Jun 25, 2026
- remove unused ProfileManager import in app-react-state-replication/App.tsx - remove unused AppModulesInitializedEvent import in AppConfigurator.ts - remove unused 'type' from event destructuring in StateProvider.ts - export PouchDbSyncStorage class - add revs_limit and size options to PouchDbStorageOptions
alftore
force-pushed
the
feat/state/add-state-module
branch
from
June 25, 2026 20:29
e3b44fd to
e6de866
Compare
Contributor
|
Added |
odinr
pushed a commit
that referenced
this pull request
Aug 4, 2026
- remove unused ProfileManager import in app-react-state-replication/App.tsx - remove unused AppModulesInitializedEvent import in AppConfigurator.ts - remove unused 'type' from event destructuring in StateProvider.ts - export PouchDbSyncStorage class - add revs_limit and size options to PouchDbStorageOptions
Switch from the deprecated useRouter (@equinor/fusion-framework-react-app/navigation) + interop RouterProvider pattern to the framework's canonical Router component from @equinor/fusion-framework-react-router, matching the app-react-router cookbook. This also removes a react-router/@remix-run/router type mismatch (RouteObject/Router) that showed up under tsc -b.
- route PouchDB replication fetch through the Fusion IHttpClient instead of native fetch + manual MSAL token acquisition - remove the non-functional sync retry circuit breaker from PouchDbSyncStorage - add missing TSDoc for StateModuleConfigurator.setName
…o app-react-state Consolidate both cookbooks into one, covering the full module-state surface (basics, profile/todo replication via PouchDbSyncStorage, sync-status UI). Replace the cookbook's hand-rolled sync-event module with the framework's native onStateSync.* events.
…nt wiring packages/react/app: add useStateSyncEvents hook subscribing to onStateSync.* events. packages/modules/state: module.ts resolved the event module via init.ref, which races against the framework's concurrent module initialization and could leave StateProvider without a valid event module reference. Resolve it via requireInstance instead, matching the hasModule/requireInstance pattern used by every other module with an optional event dependency.
Merge react-app_useAppState.md and react-app_use-state-sync-events.md into a single react-app_state-support.md changeset (fixing an incorrect major bump for what is a purely additive change). Drop module-state_lint-cleanup.md, an internal-only note about an unreleased package. Also fix a formatting regression in packages/react/app/package.json introduced earlier in this branch.
- Collapse ProfileManager from 10 files (fusion-observable reducer + action creators + 7 single-field components) into one component using plain useAppState + immutable object spreads, matching the Basics/Todos pattern. Drops the now-unused fusion-observable dep. - Remove leftover debug console.logs and a stray junk property in TodoListManager and SyncStatusMonitor. - Remove dead SyncStatus/ReplicationSettings types left over from the pre-merge hand-rolled sync implementation. - Fix stale comments/README wording implying replication or a reducer pattern is always active, when both are optional. - Amend the existing merge changeset with these follow-up bullets.
Fixes JSX/formatting diagnostics reported by reviewdog (Biome) across the app-react-state cookbook, StateModuleConfigurator, module-state's public export barrel, and the useStateSyncEvents hook + its test.
…entType StateErrorEvent (onState.error) is a general operation-error event, never emitted by observePouchDbSync and never matched by StateSyncEvent.is(). Its presence in the StateSyncEventType union misled consumers into thinking sync-scoped filters (e.g. hooks) could observe it.
The "./events" export's "types" field pointed at the typo'd "./dist/types/storeventsage/index.d.ts", breaking TypeScript resolution for @equinor/fusion-framework-module-state/events. Also add the missing "events" typesVersions entry so older TS module resolution modes resolve the subpath too.
…_sync _sync() only destructured live/retry/heartbeat/timeout and discarded any other caller-provided SyncOptions (filter, since, batches_limit, etc.) when building the options object passed to PouchDB. Spread the caller options first so only the four defaulted fields are overridden.
- Effect that subscribes via stateProvider.observeItem depended on stateProvider.observeItem (a stable method reference) instead of stateProvider itself, so the effect could fail to re-run when the provider instance changed. - setValue's updater branch received the raw, possibly-undefined value$.value instead of the defaulted value, deviating from useState-like behavior. getValue is now applied before invoking the updater, and added to the callback's dependency list.
The reduce<Record<string, any>, ...> generics added to fix inference under stricter TS settings introduced new explicit any. unknown fixes the same inference issue without weakening the type system.
The example imported enableState from @equinor/fusion-framework-app/state, but the package only exports the enabler under ./enable-state - the example would not resolve for consumers as written.
{\@link configureModules} rendered literally instead of linking because the
@ was escaped.
Set and Record keyed by symbol/number aren't JSON-serializable and don't round-trip through the default PouchDB-backed storage, contradicting the persistence contract documented for useAppState.
The file ended with a JSDoc comment describing a validation function that was never implemented, plus a stray '@see' referencing a nonexistent Zod schema. Removed both - neither documents anything that exists in the file.
Several files inside @equinor/fusion-framework-module-state imported AllowedValue/StateEventType by importing the package's own name. Before the package is built, that self-import has no dist/types/* to resolve against and breaks a clean tsc -b. Switched all of them to relative imports (../types.js, ../events/index.js).
Both packages share the same caution paragraph; 'it`s' should be 'it's'.
Added as a runtime dependency but never imported anywhere in the package.
- Example used useAppState<any[]>, undermining the type-safety message and leaking any into copy-pasted consumer code; changed to unknown[]. - A duplicate closing code fence after the Key Stability example broke Markdown rendering for the rest of the doc comment. - Fixed a backtick-instead-of-apostrophe typo in an inline comment.
- split packages/modules/state/README.md into docs/{usage,storage,events,api-reference,performance}.md
- fix broken code fence in storage docs (Monitoring Sync Progress)
- drop fictional v1->v2 migration guide (package is pre-1.0)
- add vue-press pages + sidebar children for state module docs
- add react-app-state cookbook page and sort/rename cookbooks index
Noggling
previously approved these changes
Aug 5, 2026
odinr
force-pushed
the
feat/state/add-state-module
branch
from
August 5, 2026 10:45
e611582 to
bd4498e
Compare
Noggling
approved these changes
Aug 5, 2026
Contributor
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Overview
Adds persistent, optionally-replicated application state to Fusion Framework: a new
@equinor/fusion-framework-module-statepackage, framework- and React-level enablers, auseAppStatehook that behaves likeuseStatebut persists and syncs across components, and auseStateSyncEventshook for observing replication status. Includes a cookbook(
cookbooks/app-react-state) demonstrating all of it, from zero-config local storage to CouchDBreplication.
Related
Persistent User State).
What's new
@equinor/fusion-framework-module-state(new package)StateProvider- reactive state management backed by a pluggable storage interface.PouchDbStorage- local, per-app persistent storage (PouchDB), zero configuration required.PouchDbSyncStorage- bidirectional replication with a remote CouchDB, with configurablePouchDB.Replication.SyncOptions(live, retry, heartbeat, timeout).StateEntryCreatedEvent/UpdatedEvent/DeletedEvent,StateSyncStatusEvent/ChangeEvent/CompleteEvent/ErrorEvent) dispatched through the app'seventmodule.@equinor/fusion-framework-appenableState- a thin, app-scoped convenience wrapper aroundenableStateModule.@equinor/fusion-framework-react-appenableAppState- configures the app to use the state module, with app-scoped storage keyprefixing to prevent collisions between apps.
useAppState- a persistent, cross-component-synchronized alternative touseState.useStateSyncEvents- subscribes toonStateSync.status/.change/.complete/.errorandreturns the most recent events, bounded to a given
limit.Cookbook:
cookbooks/app-react-stateMerges the earlier
app-react-stateandapp-react-state-replicationcookbooks into one, covering:useAppStatefundamentals (boolean, string, optional state).useStateSyncEvents.Runs with zero setup (local storage only);
pnpm couchdb:startopts into a local CouchDB for thereplication demo.
Checklist
tsc -b --force,biome lint,fusion-lint)