@ngxs/store 18.1.1-dev.master-2833d17 → 18.1.1-dev.master-a8f62df

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.
@@ -1,5 +1,5 @@
1
1
  import * as i0 from '@angular/core';
2
- import { Injectable, NgZone, PLATFORM_ID, Inject, InjectionToken, inject, INJECTOR, Optional, SkipSelf, ErrorHandler, ɵisPromise as _isPromise, computed, makeEnvironmentProviders, ENVIRONMENT_INITIALIZER, NgModule, APP_BOOTSTRAP_LISTENER } from '@angular/core';
2
+ import { Injectable, NgZone, PLATFORM_ID, Inject, InjectionToken, inject, INJECTOR, ErrorHandler, ɵisPromise as _isPromise, Optional, SkipSelf, computed, makeEnvironmentProviders, ENVIRONMENT_INITIALIZER, NgModule, APP_BOOTSTRAP_LISTENER } from '@angular/core';
3
3
  import { Observable, config, Subject, of, forkJoin, throwError, EMPTY, from, isObservable, shareReplay as shareReplay$1, map as map$1, catchError as catchError$1, distinctUntilChanged, take as take$1, ReplaySubject } from 'rxjs';
4
4
  import * as i1 from '@ngxs/store/internals';
5
5
  import { ɵwrapObserverCalls as _wrapObserverCalls, ɵOrderedSubject as _OrderedSubject, ɵmemoize as _memoize, ɵgetStoreMetadata as _getStoreMetadata, ɵgetSelectorMetadata as _getSelectorMetadata, ɵMETA_KEY as _META_KEY, ɵINITIAL_STATE_TOKEN as _INITIAL_STATE_TOKEN, ɵNgxsAppBootstrappedState as _NgxsAppBootstrappedState, ɵNGXS_STATE_CONTEXT_FACTORY as _NGXS_STATE_CONTEXT_FACTORY, ɵNGXS_STATE_FACTORY as _NGXS_STATE_FACTORY, ɵensureStoreMetadata as _ensureStoreMetadata, ɵMETA_OPTIONS_KEY as _META_OPTIONS_KEY, ɵensureSelectorMetadata as _ensureSelectorMetadata } from '@ngxs/store/internals';
@@ -250,38 +250,34 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.6", ngImpor
250
250
  }], ctorParameters: () => [{ type: InternalActions }, { type: InternalNgxsExecutionStrategy }] });
251
251
 
252
252
  class PluginManager {
253
- constructor(_parentManager, _pluginHandlers) {
254
- this._parentManager = _parentManager;
255
- this._pluginHandlers = _pluginHandlers;
253
+ constructor() {
256
254
  this.plugins = [];
255
+ this._parentManager = inject(PluginManager, {
256
+ optional: true,
257
+ skipSelf: true
258
+ });
259
+ this._pluginHandlers = inject(NGXS_PLUGINS, {
260
+ optional: true
261
+ });
257
262
  this.registerHandlers();
258
263
  }
259
- get rootPlugins() {
260
- return (this._parentManager && this._parentManager.plugins) || this.plugins;
264
+ get _rootPlugins() {
265
+ return this._parentManager?.plugins || this.plugins;
261
266
  }
262
267
  registerHandlers() {
263
268
  const pluginHandlers = this.getPluginHandlers();
264
- this.rootPlugins.push(...pluginHandlers);
269
+ this._rootPlugins.push(...pluginHandlers);
265
270
  }
266
271
  getPluginHandlers() {
267
272
  const handlers = this._pluginHandlers || [];
268
273
  return handlers.map((plugin) => (plugin.handle ? plugin.handle.bind(plugin) : plugin));
269
274
  }
270
- /** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.0.6", ngImport: i0, type: PluginManager, deps: [{ token: PluginManager, optional: true, skipSelf: true }, { token: NGXS_PLUGINS, optional: true }], target: i0.ɵɵFactoryTarget.Injectable }); }
275
+ /** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.0.6", ngImport: i0, type: PluginManager, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
271
276
  /** @nocollapse */ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.0.6", ngImport: i0, type: PluginManager }); }
272
277
  }
273
278
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.6", ngImport: i0, type: PluginManager, decorators: [{
274
279
  type: Injectable
275
- }], ctorParameters: () => [{ type: PluginManager, decorators: [{
276
- type: Optional
277
- }, {
278
- type: SkipSelf
279
- }] }, { type: undefined, decorators: [{
280
- type: Inject,
281
- args: [NGXS_PLUGINS]
282
- }, {
283
- type: Optional
284
- }] }] });
280
+ }], ctorParameters: () => [] });
285
281
 
286
282
  const ɵɵunhandledRxjsErrorCallbacks = new WeakMap();
287
283
  const existingHandler = config.onUnhandledError;
@@ -1025,25 +1021,25 @@ class StateContextFactory {
1025
1021
  /**
1026
1022
  * Create the state context
1027
1023
  */
1028
- createStateContext(mappedStore) {
1024
+ createStateContext(path) {
1029
1025
  const root = this._internalStateOperations.getRootStateOperations();
1030
1026
  return {
1031
1027
  getState() {
1032
1028
  const currentAppState = root.getState();
1033
- return getState(currentAppState, mappedStore.path);
1029
+ return getState(currentAppState, path);
1034
1030
  },
1035
1031
  patchState(val) {
1036
1032
  const currentAppState = root.getState();
1037
1033
  const patchOperator = simplePatch(val);
1038
- setStateFromOperator(root, currentAppState, patchOperator, mappedStore.path);
1034
+ setStateFromOperator(root, currentAppState, patchOperator, path);
1039
1035
  },
1040
1036
  setState(val) {
1041
1037
  const currentAppState = root.getState();
1042
1038
  if (isStateOperator(val)) {
1043
- setStateFromOperator(root, currentAppState, val, mappedStore.path);
1039
+ setStateFromOperator(root, currentAppState, val, path);
1044
1040
  }
1045
1041
  else {
1046
- setStateValue(root, currentAppState, val, mappedStore.path);
1042
+ setStateValue(root, currentAppState, val, path);
1047
1043
  }
1048
1044
  },
1049
1045
  dispatch(actions) {
@@ -1237,6 +1233,12 @@ class StateFactory {
1237
1233
  this._actionsSubscription = null;
1238
1234
  this._propGetter = inject(ɵPROP_GETTER);
1239
1235
  this._ngxsUnhandledErrorHandler = null;
1236
+ // Instead of going over the states list every time an action is dispatched,
1237
+ // we are constructing a map of action types to lists of action metadata.
1238
+ // If the `@@Init` action is handled in two different states, the action
1239
+ // metadata list will contain two objects that have the state `instance` and
1240
+ // method names to be used as action handlers (decorated with `@Action(InitState)`).
1241
+ this._actionTypeToMetasMap = new Map();
1240
1242
  this._states = [];
1241
1243
  this._statesByName = {};
1242
1244
  this._statePaths = {};
@@ -1277,6 +1279,11 @@ class StateFactory {
1277
1279
  return context;
1278
1280
  });
1279
1281
  }
1282
+ get actionTypeToMetasMap() {
1283
+ return this._parentFactory
1284
+ ? this._parentFactory.actionTypeToMetasMap
1285
+ : this._actionTypeToMetasMap;
1286
+ }
1280
1287
  get states() {
1281
1288
  return this._parentFactory ? this._parentFactory.states : this._states;
1282
1289
  }
@@ -1331,6 +1338,7 @@ class StateFactory {
1331
1338
  bootstrappedStores.push(stateMap);
1332
1339
  }
1333
1340
  this.states.push(stateMap);
1341
+ this.hydrateActionMetasMap(stateMap);
1334
1342
  }
1335
1343
  return bootstrappedStores;
1336
1344
  }
@@ -1377,55 +1385,53 @@ class StateFactory {
1377
1385
  // Determines whether the dispatched action has been handled, this is assigned
1378
1386
  // to `true` within the below `for` loop if any `actionMetas` has been found.
1379
1387
  let actionHasBeenHandled = false;
1380
- for (const metadata of this.states) {
1381
- const actionMetas = metadata.actions[type];
1382
- if (actionMetas) {
1383
- for (const actionMeta of actionMetas) {
1384
- const stateContext = this._stateContextFactory.createStateContext(metadata);
1385
- try {
1386
- let result = metadata.instance[actionMeta.fn](stateContext, action);
1387
- // We need to use `isPromise` instead of checking whether
1388
- // `result instanceof Promise`. In zone.js patched environments, `global.Promise`
1389
- // is the `ZoneAwarePromise`. Some APIs, which are likely not patched by zone.js
1390
- // for certain reasons, might not work with `instanceof`. For instance, the dynamic
1391
- // import returns a native promise (not a `ZoneAwarePromise`), causing this check to
1392
- // be falsy.
1393
- if (_isPromise(result)) {
1394
- result = from(result);
1395
- }
1396
- if (isObservable(result)) {
1397
- // If this observable has been completed w/o emitting
1398
- // any value then we wouldn't want to complete the whole chain
1399
- // of actions. Since if any observable completes then
1400
- // action will be canceled.
1401
- // For instance if any action handler would've had such statement:
1402
- // `handler(ctx) { return EMPTY; }`
1403
- // then the action will be canceled.
1404
- // See https://github.com/ngxs/store/issues/1568
1405
- result = result.pipe(mergeMap((value) => {
1406
- if (_isPromise(value)) {
1407
- return from(value);
1408
- }
1409
- if (isObservable(value)) {
1410
- return value;
1411
- }
1412
- return of(value);
1413
- }), defaultIfEmpty({}));
1414
- if (actionMeta.options.cancelUncompleted) {
1415
- // todo: ofActionDispatched should be used with action class
1416
- result = result.pipe(takeUntil(dispatched$.pipe(ofActionDispatched(action))));
1388
+ const actionMetas = this.actionTypeToMetasMap.get(type);
1389
+ if (actionMetas) {
1390
+ for (const actionMeta of actionMetas) {
1391
+ const stateContext = this._stateContextFactory.createStateContext(actionMeta.path);
1392
+ let result;
1393
+ try {
1394
+ result = actionMeta.instance[actionMeta.fn](stateContext, action);
1395
+ // We need to use `isPromise` instead of checking whether
1396
+ // `result instanceof Promise`. In zone.js patched environments, `global.Promise`
1397
+ // is the `ZoneAwarePromise`. Some APIs, which are likely not patched by zone.js
1398
+ // for certain reasons, might not work with `instanceof`. For instance, the dynamic
1399
+ // import returns a native promise (not a `ZoneAwarePromise`), causing this check to
1400
+ // be falsy.
1401
+ if (_isPromise(result)) {
1402
+ result = from(result);
1403
+ }
1404
+ if (isObservable(result)) {
1405
+ result = result.pipe(mergeMap((value) => {
1406
+ if (_isPromise(value)) {
1407
+ return from(value);
1417
1408
  }
1409
+ if (isObservable(value)) {
1410
+ return value;
1411
+ }
1412
+ return of(value);
1413
+ }),
1414
+ // If this observable has completed without emitting any values,
1415
+ // we wouldn't want to complete the entire chain of actions.
1416
+ // If any observable completes, then the action will be canceled.
1417
+ // For instance, if any action handler had a statement like
1418
+ // `handler(ctx) { return EMPTY; }`, then the action would be canceled.
1419
+ // See https://github.com/ngxs/store/issues/1568
1420
+ defaultIfEmpty({}));
1421
+ if (actionMeta.options.cancelUncompleted) {
1422
+ // todo: ofActionDispatched should be used with action class
1423
+ result = result.pipe(takeUntil(dispatched$.pipe(ofActionDispatched(action))));
1418
1424
  }
1419
- else {
1420
- result = of({}).pipe(shareReplay());
1421
- }
1422
- results.push(result);
1423
1425
  }
1424
- catch (e) {
1425
- results.push(throwError(e));
1426
+ else {
1427
+ result = of({}).pipe(shareReplay());
1426
1428
  }
1427
- actionHasBeenHandled = true;
1428
1429
  }
1430
+ catch (e) {
1431
+ result = throwError(e);
1432
+ }
1433
+ results.push(result);
1434
+ actionHasBeenHandled = true;
1429
1435
  }
1430
1436
  }
1431
1437
  // The `NgxsUnhandledActionsLogger` is a tree-shakable class which functions
@@ -1435,9 +1441,7 @@ class StateFactory {
1435
1441
  // The `NgxsUnhandledActionsLogger` will not be resolved by the injector if the
1436
1442
  // `NgxsDevelopmentModule` is not provided. It's enough to check whether the `injector.get`
1437
1443
  // didn't return `null` so we may ensure the module has been imported.
1438
- if (unhandledActionsLogger) {
1439
- unhandledActionsLogger.warn(action);
1440
- }
1444
+ unhandledActionsLogger?.warn(action);
1441
1445
  }
1442
1446
  if (!results.length) {
1443
1447
  results.push(of({}));
@@ -1473,6 +1477,28 @@ class StateFactory {
1473
1477
  // its lifecycle is in 'bootstrapped' state.
1474
1478
  return this.statesByName[name] && valueIsBootstrappedInInitialState;
1475
1479
  }
1480
+ hydrateActionMetasMap({ path, actions, instance }) {
1481
+ const actionTypeToMetasMap = this.actionTypeToMetasMap;
1482
+ for (const actionType of Object.keys(actions)) {
1483
+ // Initialize the map entry if it does not already exist for that
1484
+ // action type. Note that action types may overlap between states,
1485
+ // as the same action can be handled by different states.
1486
+ if (!actionTypeToMetasMap.has(actionType)) {
1487
+ actionTypeToMetasMap.set(actionType, []);
1488
+ }
1489
+ const extendedActionMetas = actionTypeToMetasMap.get(actionType);
1490
+ extendedActionMetas.push(
1491
+ // This involves combining each individual action metadata with
1492
+ // the state instance and the path—essentially everything needed
1493
+ // to invoke an action. This eliminates the need to loop over states
1494
+ // every time an action is dispatched.
1495
+ ...actions[actionType].map(actionMeta => ({
1496
+ ...actionMeta,
1497
+ path,
1498
+ instance
1499
+ })));
1500
+ }
1501
+ }
1476
1502
  /** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.0.6", ngImport: i0, type: StateFactory, deps: [{ token: i0.Injector }, { token: NgxsConfig }, { token: StateFactory, optional: true, skipSelf: true }, { token: InternalActions }, { token: InternalDispatchedActionResults }, { token: StateContextFactory }, { token: _INITIAL_STATE_TOKEN, optional: true }], target: i0.ɵɵFactoryTarget.Injectable }); }
1477
1503
  /** @nocollapse */ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.0.6", ngImport: i0, type: StateFactory }); }
1478
1504
  }
@@ -1705,7 +1731,7 @@ class LifecycleStateManager {
1705
1731
  }
1706
1732
  }
1707
1733
  _getStateContext(mappedStore) {
1708
- return this._stateContextFactory.createStateContext(mappedStore);
1734
+ return this._stateContextFactory.createStateContext(mappedStore.path);
1709
1735
  }
1710
1736
  /** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.0.6", ngImport: i0, type: LifecycleStateManager, deps: [{ token: Store }, { token: InternalStateOperations }, { token: StateContextFactory }, { token: i1.ɵNgxsAppBootstrappedState }], target: i0.ɵɵFactoryTarget.Injectable }); }
1711
1737
  /** @nocollapse */ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.0.6", ngImport: i0, type: LifecycleStateManager, providedIn: 'root' }); }
@@ -2293,7 +2319,13 @@ function provideStates(states, ...features) {
2293
2319
  * ```
2294
2320
  */
2295
2321
  function withNgxsPlugin(plugin) {
2296
- return makeEnvironmentProviders([{ provide: NGXS_PLUGINS, useClass: plugin, multi: true }]);
2322
+ return makeEnvironmentProviders([
2323
+ { provide: NGXS_PLUGINS, useClass: plugin, multi: true },
2324
+ // We should inject the `PluginManager` to retrieve `NGXS_PLUGINS` and
2325
+ // register those plugins. The plugin can be added from inside the child
2326
+ // route, so the plugin manager should be re-injected.
2327
+ { provide: ENVIRONMENT_INITIALIZER, useValue: () => inject(PluginManager), multi: true }
2328
+ ]);
2297
2329
  }
2298
2330
 
2299
2331
  /**