
Security News
Axios Maintainer Confirms Social Engineering Attack Behind npm Compromise
Axios compromise traced to social engineering, showing how attacks on maintainers can bypass controls and expose the broader software supply chain.
@mmuscat/angular-actions
Advanced tools
A tiny (1kb) state management library for Angular Composition API.
A tiny (1kb) state management library for Angular Composition API.
npm install @mmuscat/angular-actions
yarn add @mmuscat/angular-actions
Define initial state factory. Supports dependency injection.
export function getInitialState() {
return {
count: 0,
}
}
Create actions, with or without props.
const Increment = new Action("Increment")
const IncrementBy = new Action("Increment", props<{ by: number }>())
Create reducers. A list of action reducers can be added for producing the next state. Supports dependency injection.
const Count = new Reducer<number>("count", (reducer) =>
reducer.add(Increment, (state, action) => state + 1),
)
Create effects. Effects are factory functions that should return an Observable. Supports
dependency injection. Effects will receive the store injector as an argument.
function logCount(store: Store) {
const count = store(Count)
return count.pipe(tap(console.log))
}
function autoIncrement(store: Store) {
const increment = store(Increment)
return interval(1000).pipe(tap(increment))
}
Error Recovery
Effects will stop running by default when an error occurs. To prevent this from happening,
handle the error using catchError or another retry strategy. If you just want errors to be
reported while keeping an effect running, return a materialized stream from an error-producing
inner observable.
function effectWithErrors(store: Store) {
const http = inject(HttpClient)
const source = store(Count)
const result = store(ResultAction)
return source.pipe(
switchMap((count) =>
http.post("url", { count }).pipe(
map(result),
materialize(), // should be placed on an inner stream
),
),
)
}
Create a Store.
const AppStore = new Store("AppStore", {
state: getInitialState,
reducers: [Count],
effects: [logCount, autoIncrement],
})
Provide the store to root module. Additional stores can be configured for each lazy loaded module. Effects will run immediately on bootstrap.
@NgModule({
imports: [StoreModule.config(AppStore)],
})
export class AppModule {}
Create a Store.
const MyStore = new Store("MyStore", {
state: getInitialState,
reducers: [Count],
effects: [logCount, autoIncrement],
})
Provide and use the store in a component. Effects won't run until the store is injected.
function setup() {
const store = inject(MyStore)
const count = store(Count) // get value from store
const increment = store(Increment) // get dispatcher from store
return {
count,
increment,
}
}
@Component({
providers: [MyStore.Provider],
})
export class MyComponent extends ViewDef(setup) {}
Important: A note about dependency injection
You must use the store's injector to retrieve actions and values.
const count = inject(Count) // don't do this!
const store = inject(MyStore) // inject store first
const count = store(Count) // this will be the correct instance
Creates an injectable Emitter that will emit actions of a given kind, with or without data.
const Increment = new Action("Increment")
const SaveTodo = new Action("SaveTodo", props<Todo>())
Actions can be injected inside the setup function of a ViewDef or Service factory. This
returns an Emitter that be used to dispatch or listen to events.
function setup() {
const store = inject(MyStore)
const increment = store(Increment)
subscribe(increment, ({ kind }) => {
console.log(kind) // "Increment"
})
setTimeout(increment, 1000)
return {
increment,
}
}
@Component()
export class MyComponent extends ViewDef(setup) {}
Creates an injectable Value that reduces actions to produce a new state. The state of the
reducer is hydrated using the object key of the same name returned by getInitialState.
function getInitialState() {
return {
count: 0, // state key must match reducer name
}
}
const Count = new Reducer(
"count", // reducer name must match state key
(reducer) => reducer.add(Increment, (state, action) => state + 1),
// .add(OtherAction, (state, action) => etc)
)
You can also supply a list of actions to a single reducer.
const Increment = new Action("Increment", props<{ by: number }>())
const Add = new Action("Add", props<{ by: number }>())
const Count = new Reducer(count, (reducer) =>
reducer.add([Increment, Add], (state, action) => state + action.by),
)
Reducers can be injected inside the setup function of a ViewDef or Service factory. This
returns a Value that be used to get, set or observe state changes.
function setup() {
const store = inject(MyStore)
const count = store(Count)
subscribe(() => {
console.log(count()) // 0
})
return {
count,
}
}
@Component()
export class MyComponent extends ViewDef(setup) {}
Returns a typed function for producing data on an Action.
const Increment = new Action("Increment", props<{ by: number }>())
Which is equivalent to
const Increment = new Action("Increment", (data: { by: number }) => data)
FAQs
A tiny (1kb) state management library for Angular Composition API.
The npm package @mmuscat/angular-actions receives a total of 4 weekly downloads. As such, @mmuscat/angular-actions popularity was classified as not popular.
We found that @mmuscat/angular-actions demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
Axios compromise traced to social engineering, showing how attacks on maintainers can bypass controls and expose the broader software supply chain.

Security News
Node.js has paused its bug bounty program after funding ended, removing payouts for vulnerability reports but keeping its security process unchanged.

Security News
The Axios compromise shows how time-dependent dependency resolution makes exposure harder to detect and contain.