@ngxs/devtools-plugin
Version:
redux devtools plugin for @ngxs/store
186 lines (179 loc) • 8.2 kB
JavaScript
import * as i0 from '@angular/core';
import { InjectionToken, inject, Injector, NgZone, ɵglobal as _global, DestroyRef, Injectable, NgModule, makeEnvironmentProviders } from '@angular/core';
import { Store, withNgxsPlugin } from '@ngxs/store';
import { getActionTypeFromInstance, InitState } from '@ngxs/store/plugins';
import { catchError, tap } from 'rxjs';
const NGXS_DEVTOOLS_OPTIONS = new InjectionToken('NGXS_DEVTOOLS_OPTIONS');
var ReduxDevtoolsActionType;
(function (ReduxDevtoolsActionType) {
ReduxDevtoolsActionType["Dispatch"] = "DISPATCH";
ReduxDevtoolsActionType["Action"] = "ACTION";
})(ReduxDevtoolsActionType || (ReduxDevtoolsActionType = {}));
var ReduxDevtoolsPayloadType;
(function (ReduxDevtoolsPayloadType) {
ReduxDevtoolsPayloadType["JumpToAction"] = "JUMP_TO_ACTION";
ReduxDevtoolsPayloadType["JumpToState"] = "JUMP_TO_STATE";
ReduxDevtoolsPayloadType["ToggleAction"] = "TOGGLE_ACTION";
ReduxDevtoolsPayloadType["ImportState"] = "IMPORT_STATE";
})(ReduxDevtoolsPayloadType || (ReduxDevtoolsPayloadType = {}));
/**
* Adds support for the Redux Devtools extension:
* http://extension.remotedev.io/
*/
class NgxsReduxDevtoolsPlugin {
_injector = inject(Injector);
_ngZone = inject(NgZone);
_options = inject(NGXS_DEVTOOLS_OPTIONS);
devtoolsExtension = null;
globalDevtools = _global['__REDUX_DEVTOOLS_EXTENSION__'] || _global['devToolsExtension'];
unsubscribe = null;
constructor() {
this.connect();
inject(DestroyRef).onDestroy(() => {
this.unsubscribe?.();
this.globalDevtools?.disconnect();
});
}
/**
* Lazy get the store for circular dependency issues
*/
get store() {
return this._injector.get(Store);
}
/**
* Middleware handle function
*/
handle(state, action, next) {
if (!this.devtoolsExtension || this._options.disabled) {
return next(state, action);
}
return next(state, action).pipe(catchError(error => {
const newState = this.store.snapshot();
this.sendToDevTools(state, action, newState);
throw error;
}), tap(newState => {
this.sendToDevTools(state, action, newState);
}));
}
sendToDevTools(state, action, newState) {
const type = getActionTypeFromInstance(action);
// if init action, send initial state to dev tools
const isInitAction = type === InitState.type;
if (isInitAction) {
this.devtoolsExtension.init(state);
}
else {
this.devtoolsExtension.send({ ...action, action: null, type }, newState);
}
}
/**
* Handle the action from the dev tools subscription
*/
dispatched(action) {
if (action.type === ReduxDevtoolsActionType.Dispatch) {
if (action.payload.type === ReduxDevtoolsPayloadType.JumpToAction ||
action.payload.type === ReduxDevtoolsPayloadType.JumpToState) {
const prevState = JSON.parse(action.state);
// This makes the DevTools and Router plugins compatible with each other.
// We check for the existence of the `router` state and ensure it has the
// `trigger` property, confirming that it is our router state (coming from `@ngxs/router-plugin`).
// This enables a time-traveling feature, as it not only restores the state but
// also allows the `RouterState` to navigate back when the action is jumped.
if (prevState.router?.trigger) {
prevState.router.trigger = 'devtools';
}
this.store.reset(prevState);
}
else if (action.payload.type === ReduxDevtoolsPayloadType.ToggleAction) {
console.warn('Skip is not supported at this time.');
}
else if (action.payload.type === ReduxDevtoolsPayloadType.ImportState) {
const { actionsById, computedStates, currentStateIndex } = action.payload.nextLiftedState;
this.devtoolsExtension.init(computedStates[0].state);
Object.keys(actionsById)
.filter(actionId => actionId !== '0')
.forEach(actionId => this.devtoolsExtension.send(actionsById[actionId], computedStates[actionId].state));
this.store.reset(computedStates[currentStateIndex].state);
}
}
else if (action.type === ReduxDevtoolsActionType.Action) {
const actionPayload = JSON.parse(action.payload);
this.store.dispatch(actionPayload);
}
}
connect() {
if (!this.globalDevtools || this._options.disabled) {
return;
}
// The `connect` method adds a `message` event listener to communicate
// with an extension through `window.postMessage` and handle message events.
// Since we only handle two specific events, we aim to avoid unnecessary change
// detections triggered by events that the extension sends, but we don't need to handle.
this.devtoolsExtension = this._ngZone.runOutsideAngular(() => this.globalDevtools.connect(this._options));
this.unsubscribe = this.devtoolsExtension.subscribe(action => {
if (action.type === ReduxDevtoolsActionType.Dispatch ||
action.type === ReduxDevtoolsActionType.Action) {
this.dispatched(action);
}
});
}
/** @nocollapse */ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.2", ngImport: i0, type: NgxsReduxDevtoolsPlugin, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
/** @nocollapse */ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.2", ngImport: i0, type: NgxsReduxDevtoolsPlugin });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.2", ngImport: i0, type: NgxsReduxDevtoolsPlugin, decorators: [{
type: Injectable
}], ctorParameters: () => [] });
function devtoolsOptionsFactory(options) {
return {
name: 'NGXS',
...options
};
}
const USER_OPTIONS = new InjectionToken('USER_OPTIONS');
class NgxsReduxDevtoolsPluginModule {
static forRoot(options) {
return {
ngModule: NgxsReduxDevtoolsPluginModule,
providers: [
withNgxsPlugin(NgxsReduxDevtoolsPlugin),
{
provide: USER_OPTIONS,
useValue: options
},
{
provide: NGXS_DEVTOOLS_OPTIONS,
useFactory: devtoolsOptionsFactory,
deps: [USER_OPTIONS]
}
]
};
}
/** @nocollapse */ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.2", ngImport: i0, type: NgxsReduxDevtoolsPluginModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
/** @nocollapse */ static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "20.3.2", ngImport: i0, type: NgxsReduxDevtoolsPluginModule });
/** @nocollapse */ static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "20.3.2", ngImport: i0, type: NgxsReduxDevtoolsPluginModule });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.2", ngImport: i0, type: NgxsReduxDevtoolsPluginModule, decorators: [{
type: NgModule
}] });
function withNgxsReduxDevtoolsPlugin(options) {
return makeEnvironmentProviders([
withNgxsPlugin(NgxsReduxDevtoolsPlugin),
{
provide: USER_OPTIONS,
useValue: options
},
{
provide: NGXS_DEVTOOLS_OPTIONS,
useFactory: devtoolsOptionsFactory,
deps: [USER_OPTIONS]
}
]);
}
/**
* The public api for consumers of @ngxs/devtools-plugin
*/
/**
* Generated bundle index. Do not edit.
*/
export { NGXS_DEVTOOLS_OPTIONS, NgxsReduxDevtoolsPlugin, NgxsReduxDevtoolsPluginModule, withNgxsReduxDevtoolsPlugin };
//# sourceMappingURL=ngxs-devtools-plugin.mjs.map