@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 Package | Description | Test Coverage | Docs |
|---|---|---|---|
@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/react | A 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.
# Installing the @superutils/promise package
npm install @superutils/promiseOnce installed, you can import the utilities directly into your project.
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:
Clone the repository:
bashgit clone https://github.com/alien45/superutils.gitInstall 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.
bashcd superutils && npm iInstall VSCode extensions
- Press
CTRL + P - Paste
ext install dbaeumer.vscode-eslintand pressENTER
- Press
CTRL + P - Paste
ext install esbenp.prettier-vscodeand pressENTER
- Press
Scripts
The following scripts are available at the root level to help with development:
npm run buildIncrementally builds all packages in thepackages/*directory.npm run build:watchStarts the incremental build process in watch mode for all packages.npm run cleanCleans all build artifacts.npm run clean:purgeRemoves alldist/*files before cleaning all build artifacts.WARNING
This script uses
rm -rffor cleaningdistfiles. Whilenpmoften 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 docsBuilds the API documentation and starts the VitePress development server in development mode, accessible at localhost:5173npm run docs:buildBuilds the API documentation and exits.npm run docs:previewServes previously built docs at localhost:4173npm run lintLints the entire codebase using ESLint.publish:prepareTest, lint, build docs and build packages, everything needed before pushing to prod or publishing in a single command.npm startCleans and starts build process in watch mode. Uses:build:watchandclean:purgenpm testRuns the test suite. It runs thescripts/test.shscript 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),corepromisereact- 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]...Option Alias Description Default uiOpens the interactive Vitest UI. ""run1Runs tests once and exits (no watch mode). ""coverage%Generates a code coverage report. ""Note:
If
packageis 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)bashnpm test core:1 -- --passWithNoTestsOr,
bashnpm test -- --passWithNoTests core:1You can combine both environment variables and the options above. However, if there's a conflict, the options take precedence.
Examples:
Command Action 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 promisepackage with UI and coverage.npm test :%:uiSame as above UI=false npm test :uiRun test and open UI. PS: environment variable UIis overriden by:uiflag.
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 testcommand to run the test suite.Code Style: Make sure your code adheres to the existing style of the project. Run
npm run lintto 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.mdfiles.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, orResolves #123if 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.
- Use keywords like
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 modifysrcortestfiles.
- 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 likerepoordocs.
- Type: Must be one of the following:
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
bashfeat(promise): add deferred execution utilityA bug fix in the 'core' package
bashfix(core): correct type inference for isObject function. Fixes #123.A documentation update for the 'react' package
bashdocs(react): add usage examples for useDebounce hookA commit with a body and a breaking change
bashrefactor(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.