Skip to content

@superutils

@superutils is a collection of small, deliberately scoped TypeScript utilities built to solve recurring production problems without introducing hidden abstractions or unnecessary coupling.

While many of the foundational packages are dependency-free and framework-agnostic, some packages are intentionally designed to integrate closely with specific ecosystems such as React or RxJS, while still favoring explicit behavior and minimal surface area. Each package is designed to be usable on its own and makes its assumptions explicit, including any framework or runtime dependencies.

Many of these utilities have been evolving for nearly a decade across real-world codebases. The immediate predecessor to this project was the common-utils library developed and used in production at Totem Accounting, where these patterns were stress-tested under real user traffic, long-lived state, and operational constraints. @superutils is a cleaned-up, modular continuation of that work.

This library is built for engineers, myself included, who prefer simple, explicit APIs and modular & reusable tools with predictable behavior and long term maintainability.

Table of Contents

Why @superutils exists

Over the past several years, I kept encountering the same classes of problems across different products and teams: managing asynchronous behavior without losing observability, reducing repeated boilerplate around common workflows, and enforcing correctness at boundaries without obscuring control flow.

In multiple production systems, including my work at Totem Accounting, these problems were often solved ad hoc. Small helper functions would emerge, get copied across codebases, slowly diverge, and eventually become harder to reason about than the problems they were meant to solve. Existing libraries addressed parts of these needs, but often introduced opinionated abstractions, hidden state, or unnecessary coupling that made long-term maintenance harder.

Over time, certain patterns proved themselves. Utilities that were explicit, small in scope, and readable in one sitting survived repeated refactors, onboarding cycles, and real user traffic. @superutils is an extraction of those patterns into a modular, reusable form. It trades breadth and cleverness for clarity, explicit behavior, and predictable failure modes.

This library exists primarily because it reflects how I prefer to build systems myself: tools I am comfortable depending on in production, understanding fully, and maintaining over time.

The intent is to build utilities once, understand them fully, test them thoroughly, and feel comfortable reusing them across different systems and contexts.

Design philosophy

  • Utilities should be small enough to understand in one sitting
  • APIs should be explicit and unsurprising
  • Systems should be fault-tolerant, with failure remaining observable when needed
  • Defaults should be conservative and opt-in
  • Types should aid correctness without obscuring runtime behavior

Non-goals

  • Not a framework or application architecture
  • Not a monolithic utility bundle
  • No hidden global state or implicit side effects
  • No runtime magic, patching, or environment mutation
  • No attempt to abstract away fundamental complexity

Packages

This monorepo contains the following packages. Each is independently versioned and published to NPM.

NPM PackageDescriptionTest CoverageDocs
@superutils/core A collection of lightweight, dependency-free utility functions and types.
Why: prevents copy-pasted helpers from drifting across codebases.
💘 100%
View
@superutils/fetch A lightweight fetch wrapper for browsers and Node.js, designed to simplify data fetching and reduce boilerplate.
Why: address recurring production issues around retries, cancellation, and request lifecycles in a consistent, observable way.
💘 100%
View
@superutils/promise An extended Promise with additional features such as status tracking, deferred/throttled execution, timeout and retry mechanism.
Why: provide shared async primitives used across higher-level utilities.
💘 100%
View
@superutils/reactA collection of React hooks and components for common UI patterns and state management.
📝 Planned
View
@superutils/rx A set of small, focused utilities for working with RxJS observables and subjects.
Why: avoid re-implementing common RxJS subject and lifecycle patterns across codebases
📝 Planned
View

Getting Started

All packages are scoped under @superutils and will be available on the NPM registry. You can install any package using your preferred package manager.

bash
# Installing the @superutils/promise package
npm install @superutils/promise

Once installed, you can import the utilities directly into your project.

typescript
import PromisE from '@superutils/promise'

const dp = PromisE.delay(1000)
console.log({
	pending: dp.pending,
	rejected: dp.rejected,
	resolved: dp.resolved,
})
// Prints: { pending: true, resolved: false, rejected: false }
await dp // waits 1 second
console.log({
	pending: dp.pending,
	rejected: dp.rejected,
	resolved: dp.resolved,
})
// Prints: { pending: false, resolved: true, rejected: false }

For more details please read the API reference for respective packages.

Contribute

For contribution guidelines and development standards used in this project, see CONTRIBUTING.md.

License

This project is licensed under the MIT License. See the LICENSE file in each package for more details.

Contribute

These guidelines describe how this project is developed and maintained. They reflect the standards I aim to follow when making changes myself, and they apply equally to any external contributions.

Contributions are welcome. If you would like to help improve @superutils, feel free to open an issue to discuss a new feature or submit a pull request. Changes are reviewed deliberately, and discussion is encouraged before significant work begins.

Table of Contents

Development Setup

To contribute to @superutils, you'll first need to set up the monorepo on your local machine. This project uses npm workspaces to manage dependencies and link local packages.

Prerequisites:

For consistency please make sure to install/update node v25.2.1 and npm v11.6.2 or higher.

Node and npm versions are pinned to avoid subtle differences in tooling behavior, module resolution, and test results across environments.

Follow the steps below to get your environment ready:

  1. Clone the repository:

    bash
    git clone https://github.com/alien45/superutils.git
  2. Install dependencies:

    This command will install all dependencies for the root project and all packages within the workspace, and it will automatically create symbolic links between them.

    bash
    cd superutils && npm i
  3. Install VSCode extensions

    1. ESLint:
    • Press CTRL + P
    • Paste ext install dbaeumer.vscode-eslint and press ENTER
    1. Prettier - Code formatter
    • Press CTRL + P
    • Paste ext install esbenp.prettier-vscode and press ENTER

Scripts

The following scripts are available at the root level to help with development:

  • npm run build Incrementally builds all packages in the packages/* directory.

  • npm run build:watch Starts the incremental build process in watch mode for all packages.

  • npm run clean Cleans all build artifacts.

  • npm run clean:purge Removes all dist/* files before cleaning all build artifacts.

    WARNING

    This script uses rm -rf for cleaning dist files. While npm often provides cross-platform compatibility for such commands, users on Windows might need a Unix-like environment (e.g., Git Bash or WSL) for it to function correctly.

  • npm run docs Builds the API documentation and starts the VitePress development server in development mode, accessible at localhost:5173

  • npm run docs:build Builds the API documentation and exits.

  • npm run docs:preview Serves previously built docs at localhost:4173

  • npm run lint Lints the entire codebase using ESLint.

  • publish:prepare Test, lint, build docs and build packages, everything needed before pushing to prod or publishing in a single command.

  • npm start Cleans and starts build process in watch mode. Uses: build:watch and clean:purge

  • npm test Runs the test suite. It runs the scripts/test.sh script which both accepts environment variables and/or a colon-delimited command argument.

    Supported environment variables:

    • PKG: name of the package to test. Accepts:
      • * (default, test all packages),
      • core
      • promise
      • react
      • or any other directory name under packages/ directory
    • UI: start the interactive Vitest UI server and open in a browser. Accepts: true / false.
    • RUN: run test only once and exit immediately. Accepts: true / false.
    • COVERAGE: enable/disable code coverage. Accepts: true / false.

    Command Argument:

    For a more streamlined workflow, you can combine the package and options into a single, colon-delimited argument with the following structure: npm test [package][:option1][:option2]...

    OptionAliasDescriptionDefault
    uiOpens the interactive Vitest UI.""
    run1Runs tests once and exits (no watch mode).""
    coverage%Generates a code coverage report.""

    Note:

    • If package is omitted (e.g., npm test :ui), tests will run for all packages.

    • Vitest CLI flags can be passed by adding -- followed by the Vitest flag (eg: --passWithNoTests)

      bash
      npm test core:1 -- --passWithNoTests

      Or,

      bash
      npm test  -- --passWithNoTests core:1
    • You can combine both environment variables and the options above. However, if there's a conflict, the options take precedence.

      Examples:

      CommandAction
      npm testWatch and run all tests for all packages.
      npm test :1Run all tests for all packages only once and exit immediately.
      UI=true npm test promise:%Watch and test only promise package with UI and coverage.
      npm test :%:uiSame as above
      UI=false npm test :uiRun test and open UI. PS: environment variable UI is overriden by :ui flag.

Pull Request Guidelines

To ensure a smooth and effective review process, please follow these guidelines when submitting a pull request:

  • Detailed Description: Provide a clear and comprehensive description of the problem you are solving or the feature you are adding. Explain the "why" behind your changes.

  • Testing: Include tests for any new features or bug fixes, aiming for 100% code coverage. Ensure that all existing tests continue to pass. Use the npm test command to run the test suite.

  • Code Style: Make sure your code adheres to the existing style of the project. Run npm run lint to check for any linting issues.

  • Documentation: If you are adding a new feature or changing an existing one, update the relevant documentation. This includes JSDoc comments (with code examples for new/changed features) and any package-specific README.md files.

  • One PR per Feature: Keep pull requests focused on a single feature or bug fix. This makes them easier to review and merge.

  • Link to an Issue: If your pull request addresses an existing issue, please link to it in the PR description.

    • Use keywords like Closes #123, Fixes #123, or Resolves #123 if the PR fully completes the work described in the issue. This will automatically close the issue when the PR is merged.
    • For partial implementations or related work, simply reference the issue number (e.g., Related to #123) to create a link without closing the issue.
  • Work in Progress & Commit History: For work that is not yet ready for a full review, please use GitHub's Draft Pull Request feature. While it's fine to have temporary "WIP" commits on your feature branch during development, please clean up your commit history before marking the PR as "Ready for Review". Use an interactive rebase (git rebase -i) to squash temporary commits into logical, well-described units of work that adhere to our commit message guidelines.

  • Commit Messages: Follow the Conventional Commits specification. This helps maintain a clear project history and enables automated changelog generation. Each final commit message should have the following format:

    <type>(<scope>): <subject>
    <BLANK LINE>
    [optional body]
    <BLANK LINE>
    [optional footer]
    • Type: Must be one of the following:
      • feat: A new feature.
      • fix: A bug fix.
      • docs: Documentation only changes.
      • style: Changes that do not affect the meaning of the code (white-space, formatting, etc).
      • refactor: A code change that neither fixes a bug nor adds a feature.
      • perf: A code change that improves performance.
      • test: Adding missing tests or correcting existing tests.
      • build: Changes that affect the build system or external dependencies.
      • ci: Changes to our CI configuration files and scripts.
      • chore: Other changes that don't modify src or test files.
    • Scope: The full package name affected by the change (e.g., core, promise). For changes that affect the entire repository (like build scripts or documentation), you can use a general scope like repo or docs.
  • Breaking Changes: Clearly identify any breaking changes in your PR description and in the commit message footer (e.g., BREAKING CHANGE: ...), including the justification and a migration guide for users.

Here are a few examples:

  • A new feature in the 'promise' package

    bash
    feat(promise): add deferred execution utility
  • A bug fix in the 'core' package

    bash
    fix(core): correct type inference for isObject function. Fixes #123.
  • A documentation update for the 'react' package

    bash
    docs(react): add usage examples for useDebounce hook
  • A commit with a body and a breaking change

    bash
    refactor(core): rewrite deepClone to improve performance
    
    The previous implementation used recursion, which could lead to stack overflows on very deep objects. The new implementation uses an iterative approach with a stack to avoid this issue.
    
    BREAKING CHANGE: The `deepClone` function no longer clones functions or Symbols. It now only clones plain objects, arrays, and primitives.