Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .nvmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
v20.14.0
27 changes: 27 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,33 @@ All UI Components follow the same basic flow:

![Architecture](https://cloud.githubusercontent.com/assets/603426/18599404/329ca9ca-7c0d-11e6-9a02-5718a0fba8db.png)

## Recent Changes

### Upcoming - Enhanced MV3 Reliability Fixes

Building on v4.0.0, additional fixes have been added to improve reliability in Manifest V3 service workers:

#### State Initialization Fix
- **Problem**: When a service worker wakes up, `sendResponse()` doesn't work reliably in MV3, causing proxy stores to fail initialization.
- **Solution**: State provider now broadcasts the full state using `browser.runtime.sendMessage()` (which is reliable in MV3) as the primary method, with `sendResponse()` as a fallback.
- **Impact**: Proxy stores can now reliably initialize even when waking up a dormant service worker.

#### Patch Ordering Fix
- **Problem**: If state patches arrive before the initial full state, they would be applied to an empty state, resulting in broken/partial state in proxy stores.
- **Solution**: Patches are now only applied after the proxy store has received the full initial state at least once.
- **Impact**: Eliminates race conditions during proxy store initialization, ensuring state consistency.

These fixes ensure webext-redux works reliably with MV3's event-driven service worker model.

### v4.0.0 - Async Response Handling & Breaking Changes

This release fixes browser console errors and requires a breaking API change:

- **Fixed**: Eliminated "message channel closed before a response was received" errors by implementing message filtering
- **Breaking Change**: `channelName` must now be passed to `createWrapStore()` instead of `wrapStore()` to enable proper message filtering
- **Documentation**: Clarified that `createWrapStore()` must be called statically in the global scope of the service worker, following [Chrome's event listener requirements](https://developer.chrome.com/docs/extensions/get-started/tutorial/service-worker-events#step-5)


## Basic Usage ([full docs here](https://github.com/tshaddix/webext-redux/wiki))

As described in the [introduction](https://github.com/tshaddix/webext-redux/wiki/Introduction#webext-redux), there are two pieces to a basic implementation of this package.
Expand Down
8 changes: 4 additions & 4 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "webext-redux",
"version": "4.0.0",
"name": "@evinced-private/webext-redux-mv3",
"version": "4.1.0",
"description": "A set of utilities for building Redux applications in Web Extensions.",
"main": "lib/index.js",
"typings": "./index.d.ts",
Expand Down
7 changes: 6 additions & 1 deletion src/store/Store.js
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,12 @@ class Store {
break;

case PATCH_STATE_TYPE:
this.patchState(message.payload);
// MV3 FIX: Only apply patches after we've received the full state at least once.
// Patches arriving before STATE_TYPE would be applied to empty state,
// resulting in partial/broken state.
if (this.readyResolved) {
this.patchState(message.payload);
}
break;

default:
Expand Down
11 changes: 11 additions & 0 deletions src/wrap-store/wrapStore.js
Original file line number Diff line number Diff line change
Expand Up @@ -173,11 +173,22 @@ export default ({ channelName = defaultOpts.channelName } = defaultOpts) => {

/**
* State provider for content-script initialization
* MV3 FIX: sendResponse doesn't work reliably when service worker wakes up.
* We broadcast full state via sendMessage which is reliable in MV3.
*/
stateProviderListener.setListener((request, sender, sendResponse) => {
// This listener is only called with messages that pass filterStateMessages
const state = store.getState();

// MV3 FIX: Primary method - broadcast full state via sendMessage (reliable in MV3)
serializedMessagePoster({
type: STATE_TYPE,
payload: state,
channelName,
});

// Secondary: sendResponse (works for small states; proxy ignores if
// already initialized via the STATE_TYPE broadcast above)
sendResponse({
type: FETCH_STATE_TYPE,
payload: state,
Expand Down