@ngxs/devtools-plugin
Version:
redux devtools plugin for @ngxs/store
174 lines (167 loc) • 7.8 kB
JavaScript
import * as i0 from '@angular/core';
import { InjectionToken, inject, Injector, NgZone, ɵglobal as _global, Injectable, NgModule, makeEnvironmentProviders } from '@angular/core';
import { Store, withNgxsPlugin } from '@ngxs/store';
import { getActionTypeFromInstance, InitState } from '@ngxs/store/plugins';
import { catchError, tap } from 'rxjs/operators';
const NGXS_DEVTOOLS_OPTIONS = new InjectionToken('NGXS_DEVTOOLS_OPTIONS');
/**
* Adds support for the Redux Devtools extension:
* http://extension.remotedev.io/
*/
class NgxsReduxDevtoolsPlugin {
constructor() {
this._injector = inject(Injector);
this._ngZone = inject(NgZone);
this._options = inject(NGXS_DEVTOOLS_OPTIONS);
this.devtoolsExtension = null;
this.globalDevtools = _global['__REDUX_DEVTOOLS_EXTENSION__'] || _global['devToolsExtension'];
this.unsubscribe = null;
this.connect();
}
ngOnDestroy() {
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 === "DISPATCH" /* ReduxDevtoolsActionType.Dispatch */) {
if (action.payload.type === "JUMP_TO_ACTION" /* ReduxDevtoolsPayloadType.JumpToAction */ ||
action.payload.type === "JUMP_TO_STATE" /* 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 && prevState.router.trigger) {
prevState.router.trigger = 'devtools';
}
this.store.reset(prevState);
}
else if (action.payload.type === "TOGGLE_ACTION" /* ReduxDevtoolsPayloadType.ToggleAction */) {
console.warn('Skip is not supported at this time.');
}
else if (action.payload.type === "IMPORT_STATE" /* 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 === "ACTION" /* 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 === "DISPATCH" /* ReduxDevtoolsActionType.Dispatch */ ||
action.type === "ACTION" /* ReduxDevtoolsActionType.Action */) {
this.dispatched(action);
}
});
}
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.0.0", ngImport: i0, type: NgxsReduxDevtoolsPlugin, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
/** @nocollapse */ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.0.0", ngImport: i0, type: NgxsReduxDevtoolsPlugin }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.0", 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 { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.0.0", ngImport: i0, type: NgxsReduxDevtoolsPluginModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
/** @nocollapse */ static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "19.0.0", ngImport: i0, type: NgxsReduxDevtoolsPluginModule }); }
/** @nocollapse */ static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "19.0.0", ngImport: i0, type: NgxsReduxDevtoolsPluginModule }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.0", 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