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

@ugo-code/streamline.js

v1.0.6

Published

A utility module which provides straight-forward, powerful functions for working with asynchronous JavaScript

Readme

@ugo-code/streamline.js

MIT License npm version

A lightweight, powerful, and zero-dependency utility library for TypeScript and JavaScript that simplifies complex asynchronous operations, enhances array manipulations, and provides robust cryptography tools.


Overview

@ugo-code/streamline.js provides a set of straight-forward, robust, and efficient functions designed to tackle common challenges in modern web development. Whether you need to prevent redundant API calls, handle transient network errors gracefully, perform complex data analysis on arrays, or secure your data with modern cryptography, this library has you covered.

Built with TypeScript, it offers full type safety and is designed for seamless integration into any project, supporting both ES Modules and CommonJS.

Features

  • Robust Async Control:
    • singleExecution: Guarantees that an async function is only executed once at a time for a given key, preventing race conditions and redundant operations.
    • retry: Automatically retries a failing async task with a configurable exponential backoff strategy, perfect for handling unreliable network requests.
  • Powerful Array Utilities:
    • ArraySL: An extended Array class that supercharges your data manipulations with convenient getters and powerful methods.
  • Secure Cryptography:
    • encryptString / decryptString: Encrypt and decrypt strings using AES-256-GCM with a time-to-live (TTL) to prevent replay attacks.
    • hashObject: Create a deterministic SHA-256 hash of any JavaScript value, including complex nested objects, Maps, Sets, and even structures with circular references.
  • Type-Safe: Fully written in TypeScript to provide excellent autocompletion and catch errors at compile time.
  • Seamless Chaining: ArraySL methods (including native ones like .map and .filter) return an ArraySL instance, allowing for elegant and readable method chaining.
  • Lightweight & Zero-Dependency: Keeps your node_modules folder clean and your bundle size small.

Installation

npm install @ugo-code/streamline.js

or

yarn add @ugo-code/streamline.js

or

bun add @ugo-code/streamline.js

API Documentation

A quick look at the utilities this package provides. Click on any utility to see its details.


singleExecution<TResult>(taskFn, [key])

Ensures that an asynchronous task is only executed once at a time for a given unique key. If called again with the same key while the task is running, it returns the promise of the existing task.

  • taskFn: () => Promise<TResult> - The asynchronous function to execute.
  • key (optional): SerializableKey - A unique identifier for the task. Can be a string, number, object, or array. If not provided, a key is generated by hashing the function's source code.
import { singleExecution } from "@ugo-code/streamline.js/singleExecution";

async function fetchUser(userId: string) {
  return singleExecution(
    () => {
      console.log(`Fetching user ${userId}...`);
      // Imagine this is an API call
      return new Promise((resolve) =>
        setTimeout(() => resolve({ id: userId, name: "John Doe" }), 100)
      );
    },
    `user-${userId}` // Unique key for this user
  );
}

// Both calls will trigger only one "Fetching user 123..." log
Promise.all([fetchUser("123"), fetchUser("123")]);

retry<TResult>(taskFn, [options])

Executes an asynchronous task and automatically retries it with an exponential backoff strategy if it fails.

  • taskFn: () => Promise<TResult> - The asynchronous function to execute.
  • options (optional): RetryOptions
    • limit: number (default: 2) - The maximum number of retry attempts.
    • initialDelay: number (default: 0) - The initial delay in milliseconds before the first retry.
    • maxDelay: number (default: Infinity) - The maximum delay between retries.
    • onRetry: (error, attempt, delay) => void - A callback executed before each retry.
import { retry } from "@ugo-code/streamline.js/retry";

let attempt = 0;
async function fetchUnreliableData() {
  attempt++;
  console.log(`Attempt #${attempt}...`);
  if (attempt < 3) {
    throw new Error("Network error");
  }
  return { data: "Finally!" };
}

const data = await retry(fetchUnreliableData, {
  limit: 3,
  initialDelay: 100,
  onRetry: (error, attempt) => {
    console.log(`Attempt ${attempt} failed. Retrying...`);
  },
});

console.log(data); // { data: 'Finally!' }

ArraySL<T>

An extended Array class with convenient getters and powerful utility methods.

import { ArraySL } from "@ugo-code/streamline.js/array";

const numbers = new ArraySL([1, 2, 3, 4, 5]);

Getters

  • .first: Returns the first element.
  • .last: Returns the last element.
  • .random: Returns a random element.
const arr = new ArraySL([10, 20, 30]);
console.log(arr.first); // 10
console.log(arr.last); // 30

Methods

  • .duplicates([options]): Returns a new ArraySL containing duplicate elements, with configurable modes (all, first, subsequent) and an optional accessor.
  • .middle(): Returns the middle item(s) of the array based on their index, not their value. This is not a median calculation.
  • .mostFrequent([accessor]): Finds the most frequently occurring item(s), using an optional accessor function.
  • .unique([options]): Returns a new ArraySL with unique elements based on an optional accessor function.
import { ArraySL } from "@ugo-code/streamline.js/array";

const products = new ArraySL([
  { category: "A", price: 10 },
  { category: "B", price: 20 },
  { category: "A", price: 30 },
  { category: "C", price: 20 },
]);

// Get the first product from the 'A' category after sorting by price
const result = products
  .filter((p) => p.category === "A")
  .sort((a, b) => a.price - b.price).first;

console.log(result); // { category: 'A', price: 10 }

Cryptography

Provides a set of functions for secure data handling using the Web Crypto API.

encryptString(plaintext, secretKey, [options])

Encrypts a string using AES-256-GCM and embeds a Time-To-Live (TTL).

  • plaintext: string - The string to encrypt.
  • secretKey: string - The secret key for encryption.
  • options (optional): { ttl?: number | null; pbkdf2Iterations?: number } - An options object.
    • ttl: (default: 3600000ms, 1 hour) - The validity period in milliseconds. Pass null to disable expiration.
    • pbkdf2Iterations: number (default: 100000) - The number of iterations for key derivation (PBKDF2). Warning: Reducing this value weakens security.
  • Throws: CryptoError if the environment is unsupported or encryption fails.
import { encryptString } from "@ugo-code/streamline.js/crypto";

const secret = "my-super-secret-key";
// Encrypt with a 5-second TTL
const encrypted = await encryptString("Hello, World!", secret, { ttl: 5000 });
// Encrypt without an expiration
const permanent = await encryptString("This will not expire", secret, {
  ttl: null,
});

decryptString(encryptedData, secretKey)

Decrypts a string encrypted with encryptString, verifying its TTL.

  • encryptedData: string - The Base64 encoded string from encryptString.
  • secretKey: string - The same secret key used for encryption.
  • Throws: CryptoError with specific codes for different failure reasons.
import { decryptString } from "@ugo-code/streamline.js/crypto";

// Assuming 'encrypted' is from the previous example
const decrypted = await decryptString(encrypted, secret);
console.log(decrypted); // "Hello, World!"

// After 5 seconds, this would throw a CryptoError.

hashObject(obj)

Calculates a deterministic SHA-256 hash of any JavaScript value.

This function creates a consistent, canonical string representation of any object before hashing. It correctly handles complex and nested data structures, including:

  • Objects with keys in any order.
  • Arrays, Map, and Set objects (with elements in any order).
  • Primitives and special values like Date, RegExp, BigInt, Symbol, and undefined.
  • Objects with circular references (without throwing an error).

This ensures that the same logical object always produces the same hash.

  • obj: any - The value to hash.
import { hashObject } from "@ugo-code/streamline.js/crypto";

// Works with complex, nested objects
const obj1 = {
  b: { d: new Set([1, new Date(0)]), c: 3 },
  a: 2,
};
const obj2 = {
  a: 2,
  b: { c: 3, d: new Set([new Date(0), 1]) },
};

const hash1 = await hashObject(obj1);
const hash2 = await hashObject(obj2);

console.log(hash1 === hash2); // true

// Handles circular references
const circular: any = { key: "value" };
circular.self = circular;
const circularHash = await hashObject(circular);
console.log(circularHash); // Produces a consistent hash without crashing

Error Handling

The cryptography functions throw a CryptoError for specific, catchable failures.

  • CryptoError.code: A machine-readable error code.
    • UNSUPPORTED_ENVIRONMENT: The Web Crypto API is not available.
    • ENCRYPTION_FAILED: The encryption process failed.
    • DECRYPTION_FAILED: Decryption failed, likely due to a wrong key or tampered data.
    • INVALID_DATA: The encrypted payload is malformed.
    • EXPIRED: The data's TTL has passed.
import { decryptString, CryptoError } from "@ugo-code/streamline.js/crypto";

try {
  const decrypted = await decryptString(expiredData, secret);
} catch (error) {
  if (error instanceof CryptoError && error.code === "EXPIRED") {
    console.error("The data has expired!");
  } else {
    console.error("An unexpected error occurred:", error);
  }
}

Contributing

Contributions are welcome! Please feel free to submit a pull request or open an issue.

  1. Fork the repository.
  2. Create your feature branch (git checkout -b feature/AmazingFeature).
  3. Commit your changes (git commit -m 'Add some AmazingFeature').
  4. Push to the branch (git push origin feature/AmazingFeature).
  5. Open a pull request.

License

This project is licensed under the MIT License. See the LICENSE file for details.