Skip to main content
← Back to projects

@herowcode/utils

Node.js TypeScript React Next.js

Overview

@herowcode/utils was built to solve a common pain in real-world web projects: the same utility functions (date formatting, slug generation, debounce/throttle, API wrappers, YouTube helpers, image compression) being copy‑pasted across multiple repositories. Every bug fix or improvement had to be replicated manually, which increased maintenance cost and left room for subtle inconsistencies.

I implemented a single, TypeScript‑first, tree‑shakable library that groups these helpers into focused modules (/api/date/string/youtube/files/function/array/nextjs). Consumers can import from the root or use per‑module subpaths to keep bundles small. The build pipeline uses tsup to emit CJS, ESM and .d.ts declarations, with sideEffects: false configured to enable effective tree‑shaking in modern bundlers.

A key design goal was universal usage: the same library should work seamlessly in Node.js services and browser apps (including SSR). To achieve that, I rely on package.json conditional exports to expose environment‑specific implementations for sensitive modules like files and youtube. On top of that, the public API is shaped around production‑safe patterns, such as an apiClient that always returns { data, error } and encapsulates retry, backoff and token injection instead of throwing uncaught exceptions.

Today, @herowcode/utils acts as a shared foundation for multiple projects in the HerowCode ecosystem, reducing duplicated logic and allowing centralized fixes and improvements to propagate to all consumers through a single dependency update.

Key Differentiator

What makes @herowcode/utils different from “yet another utility belt” is its explicit focus on environment‑aware design and minimal bundle impact. Instead of shipping one monolithic bundle of helpers, it is structured around conditional exports and per‑module imports, so browser‑only and server‑only implementations can coexist without extra configuration on the consumer side.

Two areas in particular stand out: YouTube helpers and the HTTP client. The youtube module implements a robust three‑step fallback to fetch video metadata without requiring an API key: first it tries to parse the ytInitialPlayerResponse from the watch page HTML; if that fails, it falls back to YouTube’s oEmbed endpoint and then to NoEmbed. Requests are cached per video ID as in‑flight Promises so concurrent callers reuse the same network request. The apiClient follows a “never throw” philosophy: all operations resolve to { data, error }, with configurable retry/backoff for network and 5xx errors, async token injection and an onSignoutUnauthorized hook to centralize 401 handling.

If you need a compact, environment‑aware utility set that you can safely use in both Node and the browser, this library is deliberately optimized for that trade‑off instead of trying to be a giant catch‑all toolbox.

Architecture

  • /api module: Typed HTTP client (apiClient) with retry and exponential backoff, async token injection, optional x-forwarded-for support and an onSignoutUnauthorized callback. Includes wrappers like apiWrapper that normalize async operations to { data, error }.
  • /date module: Date/time helpers built on top of dayjs, including formatDategetRelativeTimeparseTimeSpent and timezone utilities like fixTimezoneOffsetgetCurrentDateInUTC and getDateInUTC.
  • /string module: String transformation utilities such as camelCasekebabCasesnakeCaseslugifycapitalize, truncation helpers, markdown‑to‑text conversion and time formatting (H:M:S).
  • /youtube module: Functions to extractYouTubeIdgenerateYoutubeURL, validate links, retrieve thumbnails (getYoutubeThumbnail), fetch metadata with a three‑tier fallback (getYoutubeVideoInfo) and read video duration in the browser (getYoutubeVideoDuration via the IFrame API). Includes in‑flight Promise caching per video ID.
  • /files module: Browser‑side utilities like compressImage (offscreen canvas to WebP), downloadUrl and formatBytes, plus Node‑side helpers like fileExists and fileDelete, exposed via conditional exports so each environment only sees relevant APIs.
  • /function module: Control‑flow helpers such as debouncethrottle and a safe tryCatch wrapper that turns thrown errors into { data, error } results.
  • /array module: Helpers like shuffleunique and markdownToText that operate on collections and text content.
  • /nextjs module: An OptimizedImage component tailored for Next.js projects, with react and next as peer dependencies, focusing on better DX around image optimization.

Technical Highlights

  • Conditional exports per entry point in package.json so browser vs Node implementations are selected automatically for modules like files and youtube, without custom bundler configuration.
  • Tree‑shakable package with sideEffects: false and a documented preference for per‑module imports (@herowcode/utils/date@herowcode/utils/api), keeping bundle impact low.
  • tsup‑based build that produces CJS, ESM and .d.ts declaration files, improving TypeScript developer experience and compatibility across different tooling ecosystems.
  • Error‑safe apiClient design that always returns { data, error }, includes configurable retry with backoff, supports async token injection and centralizes 401 handling via onSignoutUnauthorized.
  • Three‑tier YouTube metadata strategy (getYoutubeVideoInfo) combined with in‑flight Promise caching, enabling metadata retrieval without an API key and reducing redundant network calls.
  • Browser image compression via an offscreen canvas (compressImage), generating WebP with configurable max width and quality, while keeping the Node build free of DOM dependencies through conditional exports.
  • Quality and automation tooling including Vitest + jsdom for tests, Biome for linting/formatting and GitHub Actions for CI, ensuring consistent builds and published artifacts.