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 🙏

© 2025 – Pkg Stats / Ryan Hefner

@tnezdev/effects

v0.3.2

Published

Provide **side-effects** so your code can do _something_

Readme

@tnezdev/effects

Thesis

All applications need to interact with the outside world in some way to do anything meaningful. An application is a combination of domain-specific business logic and generalized side-effects that define what lasting effect domain events should have and where those effects should be expressed. But these side-effects do not and should not be unique to each application. We can share familiar expectations between our applications so that all of our focus can be pointed at solving problems in the business domain rather than trying to figure out what our data model should be.

We can express this as a pattern of Actions (domain specific business logic) and Effects (generalized side-effects).

Examples of possible Actions:

  • Create A New User
  • Update User Preferences
  • Add an Item to a Cart
  • Place an Order

Examples of Effects:

  • Create Data Record
  • Update Data Record
  • Enqueue A Job
  • Dequeue A Job
  • Emit a Log
  • Query External Data

@tnezdev/effects is an attempt to formally define interfaces for these side-effects.

The aim is to allow developers to reserve mental energy for domain-specific problem solving rather than having to think through implementation details for data-persistence, or some other low-level, but required work.

In short, focus on the things that differentiate your product while applying reusable patterns to your applications plumbing.

Prior Art

This is heavily inspired by the ports and adapters pattern employed by the team at hyper.io. I was inspired by their work and wanted to create something that captured some of their ideas in a very lightweight way. After some false starts, I landed on the idea of publishing only the interfaces of common application effects.

Getting Started

NPM

npm install --save-dev @tnezdev/effects

Yarn

yarn add --dev @tnezdev/effects

PNPM

pnpm add --save-dev @tnezdev/effects

Usage

As you develop your application, we suggest consolidating your business logic in self-contained, reusable actions. You may have something like:

import type { Data, Jobs, Logs } from '@tnezdev/effects'

export type CreateNewUserActionContext = {
  data: Pick<Data, 'insert'>
  jobs: Pick<Jobs, 'enqueue'>
  logs: Pick<Logs, 'emit'>
}

export type CreateNewUserActionInput = {
  email: string
  tags: string[]
}

export class CreateNewUserAction {
  private readonly context: CreateNewUserActionContext

  constructor(context: CreateNewUserActionContext) {
    this.context = context
  }

  async execute(input: CreateNewUserActionInput) {
    const user = await context.data.insert({
      type: 'user',
      version: '1.0',
      data: input,
    })

    await context.jobs.enqueue({
      name: 'build_related_user_list',
      context: { userId: user.id },
    })

    await context.logs.emit(`Created new user: ${user.id}`)

    return user
  }
}

Which can be unit-tested, completely isolated from the implementation details of any database, queue, or log service.

import {
  CreateNewUserAction,
  CreateNewUserActionContext,
  CreateNewUserActionInput,
} from './create-new-user-action'

describe('CreateNewUserAction', () => {
  test('it should create a new user record', async () => {
    // Given
    const context = createMockContext()
    const action = new CreateNewUserAction(context)
    const input: CreateNewUserActionInput = {
      email: '[email protected]',
      tags: ['beta', 'priority'],
    }

    // When
    await action.execute(input)

    // Then
    expect(context.data).toHaveBeenCalledWith({
      type: 'user',
      version: '1.0',
      data: input,
    })
  })

  test('it should enqueue a job to build a related user list', async () => {
    // Given
    const context = createMockContext()
    const action = new CreateNewUserAction(context)
    const input: CreateNewUserActionInput = {
      email: '[email protected]',
      tags: ['beta', 'priority'],
    }

    // When
    const user = await action.execute(input)

    // Then
    expect(context.jobs).toHaveBeenCalledWith({
      topic: 'build_related_user_list',
      data: JSON.stringify({ userId: user.id }),
    })
  })

  test('it should emit a log', async () => {
    // Given
    const context = createMockContext()
    const action = new CreateNewUserAction(context)
    const input: CreateNewUserActionInput = {
      email: '[email protected]',
      tags: ['beta', 'priority'],
    }

    // When
    const user = await action.execute(input)

    // Then
    expect(context.jobs).toHaveBeenCalledWith({
      topic: 'build_related_user_list',
      data: JSON.stringify({ userId: user.id }),
    })
  })
})

const DEFAULT_MOCK_INSERT_RECORD_IMPLEMENTATION = async () => ({
  id: 'USER-ID-1234',
})
function createMockContext(
  insertRecordMockImplementation = DEFAULT_MOCK_INSERT_RECORD_IMPLEMENTATION,
): CreateNewUserActionContext {
  return {
    data: {
      insert: jest.fn().mockImplementationOnce(insertRecordMockImplementation),
    },
    jobs: {
      enqueue: jest.fn(),
    },
    logs: {
      emit: jest.fn(),
    },
  }
}