npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@dannymayer/vex

v0.3.3

Published

Vex is a simple, lightweight, asynchronous state manager for JavaScript user interfaces.

Downloads

5

Readme

Vex

Vex is a simple, lightweight, asynchronous state manager for JavaScript user interfaces.

Installation

npm install @dannymayer/vex

API Overview

Manager<StateType>

state$: Observable<StateType> An Observable of the manager's state. Will emit at least one value to any subscriber.

dispatch(action: Action): void Dispatches an Action.

once(action: Action): Observable<ActionResult<StateType>> Dispatches an Action and returns an Observable of the result.

dispatches(actionType?: string): Observable<ActionResult<StateType>> Returns an Observable that emits each time an action is dispatched, and before it resolves. If an actionType is provided, filters the returned Observable to only emit dispatches of that actionType.

results(actionType?: string): Observable<ActionResult<StateType>> Returns an Observable that emits each time an action is resolved. If an actionType is provided, filters the returned Observable to only emit results of that actionType.

createManager<StateType>

parameter initialState: StateType (required) Each manager must be initialized with an initialState.

parameter options?: ManagerOptions (optional)

ManagerOptions

allowConcurrency: boolean (optional) Defaults to true; if set to false, an Action dispatched before the previous Action has resolved will be queued and executed immediately when the previous Action resolves (using RxJS's concatMap).

Action<StateType>

type: string (required) A string representing the category of the action.

reduce(state: StateType): StateType (required) The business logic associated with a synchronous Action. Analagous to a reducer function in Redux, in that it returns the new state of the manager.

resolve(state$: Observable<StateType>): Promise<StateType> | Observable<StateType> (required) The business logic associated with an asynchronous Action. Returns a Promise or Observable of the new state of the manager.

ActionResult<StateType>

state: StateType (required) A snapshot of the state at the time the ActionResult was created.

actionType: string (required)

error?: any (optional)

Configuring Redux DevTools

Vex integrates with Redux DevTools to allow you to visualize your app's state over time, including the ability to time-travel through your app's history.

To configure DevTools, simply call setUpDevTools with an optional DevToolsOptions as the only argument.

In Angular, setUpDevTools must be invoked inside of an NgZone#run callback, like so:

import { Component, NgZone } from '@angular/core'
import { setUpDevTools } from '@dannymayer/vex'

@Component(/* ... */)
export class AppComponent {
  constructor(private _ngZone: NgZone) {
    this._ngZone.run(() => setUpDevTools())
  }
}

DevToolsOptions

(all fields are optional)

name: string

maxAge: number

latency: number

actionsBlacklist: string[]

actionsWhitelist: string[]

shouldCatchErrors: boolean

logTrace: boolean

predicate: (state: any, action: any) => boolean

shallow: boolean

Background

Why Vex? The short answer: it's async by default!

The functional-reactive style enabled by RxJS has changed the way we approach asynchrony in our code, and many state management frameworks have been built that use Observables to model application state changing over time. Functional-reactive programming is also great for doing asynchronous things like HTTP requests, but I haven't seen a state management framework that embraces this at its core; support for "side-effects" always feels like an add-on.

Well, I'm vexed.

I wanted my state manager to be simple and practical, and not too prescriptive; I want my state management to feel like part of my app's architecture, rather than like something I have to build my app around. I was frustrated with the amount of boilerplate in Flux-style state management and with the fact that asynchrony felt like a second-class citizen. I knew I wanted to keep the functional-reactive style of NgRx along with the event-sourcing feel it inherits from Flux, and I loved the ergonomic, low-boilerplate implementation that Akita offers. Vex aims to check all of those boxes in one tiny, convenient interface.

"To Do" App

app.model.ts

export interface AppState {
  todos: string[]
}

export enum AppAction {
  CREATE_TODO = 'CREATE_TODO'
  DELETE_TODO = 'DELETE_TODO'
}

export const initialState: AppState = {
  todos: []
}

app.service.ts

import { HttpClient } from '@angular/common/http'
import { Injectable } from '@angular/core'
import { Manager } from '@dannymayer/vex'
import { Observable } from 'rxjs'
import { first, map, switchMap, withLatestFrom } from 'rxjs/operators'
import { AppAction, AppState } from './app.model'

@Injectable()
export class AppService {
  constructor(
    private _httpClient: HttpClient,
    private _manager: Manager<AppState>,
  ) { }

  // This method dispatches an asynchronous action.
  // `.dispatch()`, which returns `void`, can be used in place of `.once()`.
  public createTodo(todo: string): Observable<AppState> {
    return this._manager
      .once({
        type: AppAction.CREATE_TODO,
        resolve: (state$) => this._httpClient.post('/api/todo', { todo }).pipe(
          withLatestFrom(state$),
          map(([state, response]) => ({
            todos: [ ...state.todos, response ]
          })),
        ),
      })
      .pipe(map(({ state }) => state))
  }

  // This method dispatches a synchronous action, and performs its asynchronous logic
  // outside of the manager.
  public deleteTodo(todoIndex: number): Observable<AppState> {
    this._manager.dispatch({
      type: AppAction.DELETE_TODO,
      reduce: (state) => ({
        ...state,
        todos: [
          ...state.todos.slice(0, todoIndex),
          ...state.todos.slice(todoIndex + 1),
        ],
      }),
    })
    return this._httpClient.delete(`/api/todo/${todoIndex}`).pipe(
      switchMap(() => this._manager.state$),
      first(),
    )
  }
}

app.module.ts

import { HttpClientModule } from '@angular/common/http'
import { NgModule } from '@angular/core'
import { BrowserModule } from '@angular/platform-browser'
import { VexModule } from '@dannymayer/vex'
import { AppComponent } from './app.component'
import { initialState } from './app.model'
import { AppService } from './app.service'

@NgModule({
  imports: [
    BrowserModule,
    HttpClientModule,
    VexModule.forRoot(initialState),
  ],
  declarations: [ AppComponent ],
  providers: [ AppService ],
  bootstrap: [ AppComponent ],
})
export class AppModule { }