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 🙏

© 2026 – Pkg Stats / Ryan Hefner

@rolster/vinegar

v4.1.3

Published

Container package of basic classes to implement a clean architecture.

Readme

Rolster Vinegar

Container package of basic classes to implement a clean architecture.

Installation

npm i @rolster/vinegar

Configuration

You must install the @rolster/types to define package data types, which are configured by adding them to the files property of the tsconfig.json file.

{
  "files": ["node_modules/@rolster/types/index.d.ts"]
}

Overview

Vinegar provides the abstractions for a clean-architecture persistence layer built around the Unit of Work and Repository patterns. It keeps your domain (entities) decoupled from the persistence details (the ORM), which live behind a Datasource/Database pair.

This package is ORM-agnostic — it only defines the contracts. For a ready-made implementation use @rolster/vinegar-typeorm.

Core abstractions

Domain

import { Entity } from '@rolster/vinegar';

// A domain entity is identified by a uuid
class User extends Entity {
  constructor(
    uuid: string,
    public name: string,
    public email: string
  ) {
    super(uuid);
  }
}

Models — the persisted shape — are described by interfaces from types.ts:

| Interface | Shape | | --------------- | ------------------------------------------------ | | AbstractModel | { id: number } | | EditableModel | adds updatedAt?: Date | | HideableModel | adds hidden: boolean; hiddenAt?: Date (soft delete) | | Model | hideable and editable |

Helpers modelIsEditable(model) and modelIsHideable(model) are type guards over those shapes.

Repository

AbstractRepository<T> is the contract for reading/persisting domain entities:

abstract class AbstractRepository<T extends Entity> {
  abstract save(entity: T): Promise<void>;
  abstract findOptionalByUuid(uuid: string): Promise<Optional<T>>;
  abstract findAll(): Promise<T[]>;
  abstract destroy(entity: T): Promise<void>;
}

(Optional comes from @rolster/commons.)

Mapping entity ↔ model

You describe how an entity is translated into persistence operations through these abstract classes:

  • EntityPersist<E, M> — create a model from an entity: create(manager): M
  • EntityPersistList<E> — create several models at once.
  • EntitySync<E, M> — update an existing model; it captures the model's initial state in the constructor so only the changed fields are persisted (sync(manager) mutates the model, verify() returns the diff).
  • EntityRefresh<E, M> — reload fresh data into a model: dispatch(manager).

Unit of Work — EntityManager

AbstractEntityManager queues operations and flushes them as a batch:

abstract class AbstractEntityManager {
  persist(persist: EntityPersist): void;       // queue a create
  persists(persists: EntityPersistList): void;  // queue multiple creates
  sync(sync: EntitySync): void;                 // queue an update (diff-based)
  refresh(refresh: EntityRefresh): void;        // queue a reload
  destroy(entity: AbstractEntity): void;        // queue a delete (or soft-hide)
  procedure(procedure: AbstractProcedure): void;// queue a custom operation
  relation(entity, model): void;                // link an entity to its model
  link<E>(entity: E, model): E;                 // link and return the entity
  select<M>(entity): Result<M>;                 // get the model linked to an entity
  flush(): Promise<PersistentUnitResult[]>;     // run everything in order
  dispose(): void;                              // clear the queue
}

Persistence backend

  • AbstractEntityDatabase — connection & transaction lifecycle: connect, disconnect, transaction, commit, rollback.
  • AbstractEntityDataSource — the low-level operations the manager delegates to: insert, update, refresh, delete, hidden, procedure.
  • AbstractPersistentUnit — coordinates a database + manager so a flush() runs inside a single transaction.
  • AbstractProcedure — wraps a custom database operation (execute(...args)).

Operation result

Every operation produces a PersistentUnitResult:

class PersistentUnitResult {
  code: 'insert' | 'update' | 'refresh' | 'delete' | 'hidden' | 'procedure' | 'operation';
  error: any; // null/undefined when the operation succeeded
  model?: AbstractModel;
}

flush() returns the array of results, so you can inspect which operations failed.

Flow

  1. The domain layer works only with Entity objects.
  2. To persist changes you queue EntityPersist / EntitySync / EntityRefresh instances (or a destroy/procedure) into the EntityManager.
  3. flush() runs the queued operations through the Datasource, inside a transaction managed by the Database/PersistentUnit, and returns a PersistentUnitResult[].

For a concrete, ready-to-use wiring of all these abstractions see @rolster/vinegar-typeorm.

Contributing

  • Daniel Andrés Castillo Pedroza :rocket: