ngx-statewise
Version:
A lightweight and intuitive state management for Angular. Simpler than NgRx, more structured than DIY.
786 lines (749 loc) • 33.5 kB
JavaScript
import * as i0 from '@angular/core';
import { Injectable, inject, signal, runInInjectionContext, provideEnvironmentInitializer, EnvironmentInjector, makeEnvironmentProviders, provideAppInitializer } from '@angular/core';
import { isObservable, firstValueFrom } from 'rxjs';
/**
* Represents an empty payload for actions without data.
*
* @returns `undefined`
*/
const emptyPayload = () => undefined;
/**
* Creates a typed identity function for payloads.
*
* Useful to define payload handlers with a specific type.
*
* @template T - The expected payload type.
* @returns A function that returns the same payload it receives.
*/
function payload() {
return (p) => p;
}
/**
* Converts a camelCase or PascalCase string to SCREAMING_SNAKE_CASE.
*
* @param str - The input string.
* @returns The same string converted to SCREAMING_SNAKE_CASE.
*
* @example
* toScreamingSnakeCase('loadFailure') // 'LOAD_FAILURE'
* toScreamingSnakeCase('UserLogin') // 'USER_LOGIN'
*/
function toScreamingSnakeCase(str) {
return str.replace(/([a-z])([A-Z])/g, '$1_$2').toUpperCase();
}
function ofType(action) {
if (typeof action === 'function') {
return action.type;
}
return action.type;
}
class ActionCreatorFactory {
static defineActionCreator(type, payloadFn) {
let fn;
if (payloadFn === emptyPayload) {
fn = () => this.createAction(type);
}
else {
fn = (payload) => this.createAction(type, payloadFn(payload));
}
fn.type = type;
return fn;
}
static createAction(type, payload) {
return payload === undefined ? { type } : { type, payload };
}
}
class ActionGroupHandler {
static handle(config) {
const { source, events } = config;
const result = {};
for (const key in events) {
const type = `${source.toUpperCase()}_${toScreamingSnakeCase(key)}`;
result[key] = ActionCreatorFactory.defineActionCreator(type, events[key]);
}
return result;
}
}
class SingleActionHandler {
static handle(source, payload) {
return {
action: ActionCreatorFactory.defineActionCreator(`${source}_ACTION`, payload),
};
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.2", ngImport: i0, type: SingleActionHandler, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.0.2", ngImport: i0, type: SingleActionHandler, providedIn: 'root' });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.2", ngImport: i0, type: SingleActionHandler, decorators: [{
type: Injectable,
args: [{ providedIn: 'root' }]
}] });
class ActionService {
static defineActionsGroup(config) {
return ActionGroupHandler.handle(config);
}
static defineSingleAction(source, payload) {
return SingleActionHandler.handle(source, payload);
}
}
/**
* Defines a group of related actions with a common source
*
* This function provides a convenient way to define action groups without
* directly injecting the ActionService
*
* @param config - Object containing source prefix and events map
* @returns An object with action creators for each event
*/
function defineActionsGroup(config) {
return ActionService.defineActionsGroup(config);
}
/**
* Defines a single action with a source prefix
*
* This function provides a convenient way to define a single action without
* directly injecting the ActionService
*
* @param source - Source prefix for the action type
* @param payload - Payload handler function
* @returns An object with an action creator
*/
function defineSingleAction(source, payload) {
return ActionService.defineSingleAction(source, payload);
}
class EffectRelationRegistery {
actionRelations = new Map();
register(parent, child) {
if (!this.actionRelations.has(parent)) {
this.actionRelations.set(parent, new Set());
}
this.actionRelations.get(parent)?.add(child);
}
getAllRelated(action, visited = new Set()) {
if (visited.has(action))
return visited;
visited.add(action);
const children = this.actionRelations.get(action) || new Set();
for (const child of children) {
this.getAllRelated(child, visited);
}
return visited;
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.2", ngImport: i0, type: EffectRelationRegistery, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.0.2", ngImport: i0, type: EffectRelationRegistery, providedIn: 'root' });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.2", ngImport: i0, type: EffectRelationRegistery, decorators: [{
type: Injectable,
args: [{ providedIn: 'root' }]
}] });
class PendingEffectRegistry {
pending = new Map();
effectRelationRegistery = inject(EffectRelationRegistery);
register(actionType, promise) {
const list = this.pending.get(actionType) || [];
this.pending.set(actionType, [...list, promise]);
promise.finally(() => {
const current = this.pending.get(actionType) || [];
this.pending.set(actionType, current.filter((p) => p !== promise));
});
return promise;
}
get(actionType) {
return this.pending.get(actionType) || [];
}
async waitFor(actionType) {
const allRelatedTypes = this.effectRelationRegistery.getAllRelated(actionType);
const allPromisesToWait = [];
for (const type of allRelatedTypes) {
const promises = this.pending.get(type) || [];
allPromisesToWait.push(...promises);
}
await Promise.all(allPromisesToWait);
}
async waitForAll() {
const all = Array.from(this.pending.values()).flat();
await Promise.all(all);
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.2", ngImport: i0, type: PendingEffectRegistry, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.0.2", ngImport: i0, type: PendingEffectRegistry, providedIn: 'root' });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.2", ngImport: i0, type: PendingEffectRegistry, decorators: [{
type: Injectable,
args: [{ providedIn: 'root' }]
}] });
class EffectResultResolver {
async resolve(result) {
if (result === undefined || result === null) {
return [undefined];
}
if (result instanceof Promise) {
const awaited = await result;
if (isObservable(awaited)) {
const resolvedObs = await firstValueFrom(awaited);
return Array.isArray(resolvedObs) ? resolvedObs : [resolvedObs];
}
return Array.isArray(awaited) ? awaited.flat() : [awaited];
}
if (isObservable(result)) {
const resolved = await firstValueFrom(result);
return Array.isArray(resolved) ? resolved : [resolved];
}
if (Array.isArray(result)) {
return result;
}
return [result];
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.2", ngImport: i0, type: EffectResultResolver, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.0.2", ngImport: i0, type: EffectResultResolver, providedIn: 'root' });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.2", ngImport: i0, type: EffectResultResolver, decorators: [{
type: Injectable,
args: [{ providedIn: 'root' }]
}] });
class GlobalUpdatorsRegistry {
_updatorRegistry = new Map();
constructor() { }
registerUpdator(actionType, updator) {
if (this._updatorRegistry.has(actionType)) {
return;
}
this._updatorRegistry.set(actionType, updator);
}
registerFullUpdator(updator) {
Object.keys(updator.updators).forEach((actionType) => {
this.registerUpdator(actionType, updator);
});
}
getUpdator(actionType) {
return this._updatorRegistry.get(actionType);
}
getRegisteredActionTypes() {
return Array.from(this._updatorRegistry.keys());
}
hasUpdator(actionType) {
return this._updatorRegistry.has(actionType);
}
unregisterUpdator(actionType) {
return this._updatorRegistry.delete(actionType);
}
clearUpdators() {
this._updatorRegistry.clear();
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.2", ngImport: i0, type: GlobalUpdatorsRegistry, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.0.2", ngImport: i0, type: GlobalUpdatorsRegistry, providedIn: 'root' });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.2", ngImport: i0, type: GlobalUpdatorsRegistry, decorators: [{
type: Injectable,
args: [{ providedIn: 'root' }]
}], ctorParameters: () => [] });
class ActionContextRegistery {
contextMap = new Map();
set(actionType, context) {
this.contextMap.set(actionType, context);
}
get(actionType) {
return this.contextMap.get(actionType);
}
clear(actionType) {
this.contextMap.delete(actionType);
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.2", ngImport: i0, type: ActionContextRegistery, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.0.2", ngImport: i0, type: ActionContextRegistery, providedIn: 'root' });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.2", ngImport: i0, type: ActionContextRegistery, decorators: [{
type: Injectable,
args: [{ providedIn: 'root' }]
}] });
class LocalUpdatorRegistry {
localRegistries = new WeakMap();
register(manager, updator) {
if (!this.localRegistries.has(manager)) {
this.localRegistries.set(manager, new Set());
}
const localRegistry = this.localRegistries.get(manager);
localRegistry.add(updator);
}
get(manager, actionType) {
const localRegistry = this.localRegistries.get(manager);
if (!localRegistry)
return undefined;
for (const updator of localRegistry) {
if (updator.updators[actionType]) {
return updator;
}
}
return undefined;
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.2", ngImport: i0, type: LocalUpdatorRegistry, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.0.2", ngImport: i0, type: LocalUpdatorRegistry, providedIn: 'root' });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.2", ngImport: i0, type: LocalUpdatorRegistry, decorators: [{
type: Injectable,
args: [{ providedIn: 'root' }]
}] });
/**
* Updates the given state based on the action and the corresponding `updator`.
*
* This function looks up the `Updator` function associated with the action type
* from the provided `updators` registry and applies it to update the state.
* If no handler is found for the action type, a warning is logged.
*
* @template S - The type of the state to be updated.
* @param state - The current state to be updated.
* @param action - The action that triggered the state update.
* @param updators - A registry of action types to their respective `Updator` functions.
*/
function update(state, action, updators) {
const handler = updators[action.type];
if (handler) {
handler(state, action.payload);
}
else {
console.warn(`No handler for action type: ${action.type}`);
}
}
class ActionEffectRegistry {
_effects = new Map();
register(actionType, effect) {
const list = this._effects.get(actionType) || [];
this._effects.set(actionType, [...list, effect]);
}
get(actionType) {
return this._effects.get(actionType) || [];
}
has(actionType) {
return !!this._effects.get(actionType)?.length;
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.2", ngImport: i0, type: ActionEffectRegistry, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.0.2", ngImport: i0, type: ActionEffectRegistry, providedIn: 'root' });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.2", ngImport: i0, type: ActionEffectRegistry, decorators: [{
type: Injectable,
args: [{ providedIn: 'root' }]
}] });
class ActionHistoryService {
_latest = signal(null);
_history = signal([]);
latest() {
return this._latest.asReadonly();
}
history() {
return this._history.asReadonly();
}
record(action) {
this._latest.set(action);
this._history.update((list) => [...list, action]);
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.2", ngImport: i0, type: ActionHistoryService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.0.2", ngImport: i0, type: ActionHistoryService, providedIn: 'root' });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.2", ngImport: i0, type: ActionHistoryService, decorators: [{
type: Injectable,
args: [{ providedIn: 'root' }]
}] });
class ActionEffectHandlerr {
registry = inject(ActionEffectRegistry);
handle(action) {
const effects = this.registry.get(action.type);
for (const effect of effects) {
effect(action);
}
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.2", ngImport: i0, type: ActionEffectHandlerr, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.0.2", ngImport: i0, type: ActionEffectHandlerr, providedIn: 'root' });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.2", ngImport: i0, type: ActionEffectHandlerr, decorators: [{
type: Injectable,
args: [{ providedIn: 'root' }]
}] });
class ActionDispatcherService {
actionHistory = inject(ActionHistoryService);
actionEffectHandler = inject(ActionEffectHandlerr);
globalEffectRegistery = inject(ActionEffectRegistry);
pendingEffectRegistry = inject(PendingEffectRegistry);
emit(action) {
if (!this.globalEffectRegistery.has(action.type)) {
this.pendingEffectRegistry.register(action.type, Promise.resolve());
}
this.actionHistory.record(action);
this.actionEffectHandler.handle(action);
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.2", ngImport: i0, type: ActionDispatcherService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.0.2", ngImport: i0, type: ActionDispatcherService, providedIn: 'root' });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.2", ngImport: i0, type: ActionDispatcherService, decorators: [{
type: Injectable,
args: [{ providedIn: 'root' }]
}] });
class CoordinatorService {
pendingEffectRegistry = inject(PendingEffectRegistry);
actionDispatcher = inject(ActionDispatcherService);
dispatch(action, updator) {
update(updator.state, action, updator.updators);
this.actionDispatcher.emit(action);
}
dispatchAsync(action, updator) {
this.dispatch(action, updator);
return this.pendingEffectRegistry.waitFor(action.type);
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.2", ngImport: i0, type: CoordinatorService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.0.2", ngImport: i0, type: CoordinatorService, providedIn: 'root' });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.2", ngImport: i0, type: CoordinatorService, decorators: [{
type: Injectable,
args: [{ providedIn: 'root' }]
}] });
class UpdatorResolver {
globalUpdatorsRegistry = inject(GlobalUpdatorsRegistry);
localUpdatorsRegistry = inject(LocalUpdatorRegistry);
actionContext = inject(ActionContextRegistery);
resolveUpdator(actionType, contextOrUpdator) {
this.setContext(actionType, contextOrUpdator);
const explicit = this.asUpdator(contextOrUpdator);
if (explicit) {
this.localUpdatorsRegistry.register(explicit, explicit);
return explicit;
}
const local = this.asLocal(contextOrUpdator, actionType);
if (local) {
return local;
}
return this.globalUpdatorsRegistry.getUpdator(actionType);
}
setContext(type, context) {
if (context)
this.actionContext.set(type, context);
}
asUpdator(updator) {
return updator && 'state' in updator && 'updators' in updator
? updator
: null;
}
asLocal(context, type) {
return context && !this.asUpdator(context)
? this.localUpdatorsRegistry.get(context, type) ?? null
: null;
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.2", ngImport: i0, type: UpdatorResolver, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.0.2", ngImport: i0, type: UpdatorResolver, providedIn: 'root' });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.2", ngImport: i0, type: UpdatorResolver, decorators: [{
type: Injectable,
args: [{ providedIn: 'root' }]
}] });
class DispatchHandler {
coordinator = inject(CoordinatorService);
actionDispatcher = inject(ActionDispatcherService);
updatorResolver = inject(UpdatorResolver);
handle(action, contextOrUpdator) {
const updator = this.updatorResolver.resolveUpdator(action.type, contextOrUpdator);
if (updator) {
this.coordinator.dispatch(action, updator);
}
else {
this.actionDispatcher.emit(action);
}
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.2", ngImport: i0, type: DispatchHandler, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.0.2", ngImport: i0, type: DispatchHandler, providedIn: 'root' });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.2", ngImport: i0, type: DispatchHandler, decorators: [{
type: Injectable,
args: [{ providedIn: 'root' }]
}] });
class EffectResultHandler {
actionDispatcher = inject(ActionDispatcherService);
pendingEffectRegistry = inject(PendingEffectRegistry);
actionContextRegistry = inject(ActionContextRegistery);
effectRelationRegistry = inject(EffectRelationRegistery);
globalUpdatorsRegistry = inject(GlobalUpdatorsRegistry);
localUpdatorRegistry = inject(LocalUpdatorRegistry);
dispatch = inject(DispatchHandler);
async handle(results, parentActionType) {
const subActionPromises = [];
const context = this.actionContextRegistry.get(parentActionType);
for (const result of results.flat().filter((a) => !!a)) {
this.effectRelationRegistry.register(parentActionType, result.type);
let used = this.tryUseLocalUpdator(result, context);
if (!used) {
used = this.tryUseGlobalUpdator(result);
}
if (!used) {
this.actionDispatcher.emit(result);
}
this.collectPendingPromises(result, subActionPromises);
}
this.actionContextRegistry.clear(parentActionType);
return subActionPromises;
}
tryUseLocalUpdator(action, context) {
if (!context)
return false;
const local = this.localUpdatorRegistry.get(context, action.type);
if (local) {
this.dispatch.handle(action, context);
return true;
}
return false;
}
tryUseGlobalUpdator(action) {
const globalUpdator = this.globalUpdatorsRegistry.getUpdator(action.type);
if (globalUpdator) {
this.dispatch.handle(action, globalUpdator);
return true;
}
return false;
}
collectPendingPromises(action, subActionPromises) {
const pending = this.pendingEffectRegistry.get(action.type);
if (pending.length) {
subActionPromises.push(pending[0]);
}
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.2", ngImport: i0, type: EffectResultHandler, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.0.2", ngImport: i0, type: EffectResultHandler, providedIn: 'root' });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.2", ngImport: i0, type: EffectResultHandler, decorators: [{
type: Injectable,
args: [{ providedIn: 'root' }]
}] });
class EffectPromiseService {
effectResultResolver = inject(EffectResultResolver);
effectResultHandler = inject(EffectResultHandler);
createPromise(handler, action, actionType) {
return (async () => {
try {
const rawResult = handler(action.payload);
const results = await this.effectResultResolver.resolve(rawResult);
const subActionPromises = await this.effectResultHandler.handle(results, actionType);
if (subActionPromises.length > 0) {
await Promise.all(subActionPromises);
}
}
catch (error) {
console.error(`Effect for ${actionType} failed:`, error);
}
})();
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.2", ngImport: i0, type: EffectPromiseService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.0.2", ngImport: i0, type: EffectPromiseService, providedIn: 'root' });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.2", ngImport: i0, type: EffectPromiseService, decorators: [{
type: Injectable,
args: [{ providedIn: 'root' }]
}] });
class EffectRegistrationService {
globalEffectRegistry = inject(ActionEffectRegistry);
pendingEffectRegistry = inject(PendingEffectRegistry);
effectPromiseService = inject(EffectPromiseService);
registerEffect(actionCreator, handler) {
const actionType = ofType(actionCreator);
this.globalEffectRegistry.register(actionType, async (action) => {
const effectPromise = this.effectPromiseService.createPromise(handler, action, actionType);
this.pendingEffectRegistry.register(actionType, effectPromise);
return effectPromise;
});
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.2", ngImport: i0, type: EffectRegistrationService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.0.2", ngImport: i0, type: EffectRegistrationService, providedIn: 'root' });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.2", ngImport: i0, type: EffectRegistrationService, decorators: [{
type: Injectable,
args: [{ providedIn: 'root' }]
}] });
let rootInjector = null;
function setRootInjector(injector) {
rootInjector = injector;
}
function getRootInjector() {
if (!rootInjector) {
throw new Error('[StateManager] EnvironmentInjector not initialized. Ensure that provideStateManager() is included in the providers of bootstrapApplication.');
}
return rootInjector;
}
async function withInjectionContext(fn) {
return runInInjectionContext(getRootInjector(), fn);
}
/**
* Internal implementation of createEffect, handling both payload and no-payload cases.
*/
function createEffect(action, handler) {
withInjectionContext(() => {
const effectRegistrationService = inject(EffectRegistrationService);
effectRegistrationService.registerEffect(action, handler);
});
}
/**
* Waits for all currently pending effects to resolve.
*
* This function ensures that all side effects triggered by actions are completed
* before continuing with the execution flow.
*
* @returns A promise that resolves when all pending effects are completed.
*/
function waitForAllEffects() {
return withInjectionContext(() => {
const pendingEffect = inject(PendingEffectRegistry);
return pendingEffect.waitForAll();
});
}
/**
* Waits for pending effects associated with a specific action type to resolve.
*
* Useful for scenarios where you need to wait for effects triggered by a particular
* action before proceeding.
*
* @param actionType - The action type whose effects should be waited for.
* @returns A promise that resolves when all pending effects for the specified action type are completed.
*/
function waitForEffect(actionType) {
return withInjectionContext(() => {
const pendingEffect = inject(PendingEffectRegistry);
return pendingEffect.waitFor(actionType);
});
}
class DispatchService {
dispatchHandler = inject(DispatchHandler);
dispatch(action, contextOrUpdator) {
this.dispatchHandler.handle(action, contextOrUpdator);
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.2", ngImport: i0, type: DispatchService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.0.2", ngImport: i0, type: DispatchService, providedIn: 'root' });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.2", ngImport: i0, type: DispatchService, decorators: [{
type: Injectable,
args: [{ providedIn: 'root' }]
}] });
function dispatch(action, contextOrUpdator) {
withInjectionContext(() => {
const dispatchService = inject(DispatchService);
dispatchService.dispatch(action, contextOrUpdator);
});
}
class DispatchAsyncHandler {
coordinator = inject(CoordinatorService);
actionDispatcher = inject(ActionDispatcherService);
pendingEffect = inject(PendingEffectRegistry);
actionContext = inject(ActionContextRegistery);
updatorResolver = inject(UpdatorResolver);
handle(action, contextOrUpdator) {
const updator = this.updatorResolver.resolveUpdator(action.type, contextOrUpdator);
if (updator) {
return this.execWithCleanup(this.coordinator.dispatchAsync(action, updator), action.type);
}
this.actionDispatcher.emit(action);
return this.execWithCleanup(this.pendingEffect.waitFor(action.type), action.type);
}
async execWithCleanup(promise, type) {
try {
return await promise;
}
finally {
this.actionContext.clear(type);
}
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.2", ngImport: i0, type: DispatchAsyncHandler, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.0.2", ngImport: i0, type: DispatchAsyncHandler, providedIn: 'root' });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.2", ngImport: i0, type: DispatchAsyncHandler, decorators: [{
type: Injectable,
args: [{ providedIn: 'root' }]
}] });
class DispatchAsyncService {
dispatchAsynHandler = inject(DispatchAsyncHandler);
dispatchAsync(action, contextOrUpdator) {
return this.dispatchAsynHandler.handle(action, contextOrUpdator);
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.2", ngImport: i0, type: DispatchAsyncService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.0.2", ngImport: i0, type: DispatchAsyncService, providedIn: 'root' });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.2", ngImport: i0, type: DispatchAsyncService, decorators: [{
type: Injectable,
args: [{ providedIn: 'root' }]
}] });
function dispatchAsync(action, contextOrUpdator) {
return withInjectionContext(() => {
const dispatchAsyncService = inject(DispatchAsyncService);
return dispatchAsyncService.dispatchAsync(action, contextOrUpdator);
});
}
class UpdatorRegistrationService {
localRegistry = inject(LocalUpdatorRegistry);
registerLocalUpdator(manager, updator) {
this.localRegistry.register(manager, updator);
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.2", ngImport: i0, type: UpdatorRegistrationService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.0.2", ngImport: i0, type: UpdatorRegistrationService, providedIn: 'root' });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.2", ngImport: i0, type: UpdatorRegistrationService, decorators: [{
type: Injectable,
args: [{ providedIn: 'root' }]
}] });
/**
* Registers a local updater for a given manager context.
*
* This function should be used within an Angular injection context (e.g. during component initialization).
*
* @template S - The state type handled by the updater.
* @param {object} manager - The local context object (e.g., a component or a service) to associate the updater with.
* @param {IUpdator<S>} updator - The updater instance defining actions and their update logic for the given state.
*/
function registerLocalUpdator(manager, updator) {
withInjectionContext(() => {
const updatorRegistrationService = inject(UpdatorRegistrationService);
updatorRegistrationService.registerLocalUpdator(manager, updator);
});
}
function provideStatewise() {
return provideEnvironmentInitializer(() => {
const injector = inject(EnvironmentInjector);
setRootInjector(injector);
});
}
/**
* Registers and instantiates a list of effect classes at application startup.
*
* @param effectClasses - Array of effect class types to initialize.
* @returns EnvironmentProviders to include in your application bootstrap.
*/
function provideEffects(effectClasses) {
return makeEnvironmentProviders([
...effectClasses.map((effectClass) => ({
provide: effectClass,
useClass: effectClass,
})),
provideEnvironmentInitializer(() => {
effectClasses.forEach((effectClass) => inject(effectClass));
}),
]);
}
/**
* Registers an array of updators globally at application startup.
*
* This function provides a way to register state updators during the Angular
* application initialization phase, making them available throughout the application
* without needing to register them with each dispatch call.
*
* @param updators - An array of Updator class to be registered globally.
* @returns Angular EnvironmentProviders to be included in your application bootstrap.
*
*/
function provideUpdators(updatorClasses) {
return makeEnvironmentProviders([
...updatorClasses, // ensure classes are registered as providers
provideAppInitializer(() => {
const registry = inject(GlobalUpdatorsRegistry);
updatorClasses.forEach((cls) => {
const instance = inject(cls);
registry.registerFullUpdator(instance);
});
}),
]);
}
/*
* Public API Surface of ngx-statewise
*/
/**
* Generated bundle index. Do not edit.
*/
export { createEffect, defineActionsGroup, defineSingleAction, dispatch, dispatchAsync, emptyPayload, ofType, payload, provideEffects, provideStatewise, provideUpdators, registerLocalUpdator, waitForAllEffects, waitForEffect };
//# sourceMappingURL=ngx-statewise.mjs.map