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

@worktif/utils

v0.2.27

Published

TypeScript utilities library providing dependency injection, logging, exception handling, and Zod validation tools with AWS Lambda integration

Readme

@worktif/utils

npm version Node Support TypeScript License

Overview

@worktif/utils is a TypeScript-first utility toolkit for enterprise Node.js, CLI and AWS Lambda applications. It provides composable primitives for logging, dependency injection, decorators, exception handling, process/CLI ergonomics, and schema-friendly I/O–engineered for low-latency paths, high observability, and production safety.

The library emphasizes:

  • Deterministic behavior under concurrency
  • Explicit error semantics and structured logs
  • Composability via a minimal DI core
  • Zero-cost abstractions in hot paths

Key Features

  • Production logger
    • Built on @aws-lambda-powertools/logger with custom formatter and serializers
    • Sync/async payload serialization, structured fields, level-aware emission
    • Stage-aware defaults and service name namespacing
  • Lightweight DI container (PureContainer)
    • Factory and constant bindings, explicit dependency graphs
    • Deterministic construction order and optional guarded arguments
  • Decorators for orchestration
    • Pre/post injectors, safe error interception hooks
    • Request-scoped context injection (before-instance pattern)
  • Exception utilities
    • Custom exceptions and typed error surfaces for predictable handling
  • Common utilities
    • ANSI-safe CLI logs, identity helpers, safe object access, small functional helpers
  • Cloud-native ergonomics
    • First-class Lambda readiness, zero-dependency bootstraps, environment-driven configuration

Installation

npm install @worktif/utils
yarn add @worktif/utils

Peer requirements

  • Node.js >= 20
  • TypeScript 5.8.x recommended
  • reflect-metadata must be imported once at app entry
  • If using DI/decorators, enable "emitDecoratorMetadata": true and "experimentalDecorators": true in tsconfig.json

Usage

  1. Logger: structured, serializer-aware logs
import 'reflect-metadata';
import { logger, initLog, LoggerLevel } from '@worktif/utils';

// Configure a namespaced logger
const appLogger = logger({ serviceName: 'inventory/worker' });

async function main() {
  const log = await initLog(appLogger, 'ReconcileInventory', LoggerLevel.Info);

  // Log immediate payloads
  log.now({ sku: 'A-123', delta: 7 }, { tag: 'inventoryChange' });

  // Log future/async results with an async serializer
  const result = await log.future(
    Promise.resolve({ requestId: 'req-1', status: 'ok', items: 120 }),
    {
      serializer: async () => (payload: any) => ({
        ...payload,
        isoTime: new Date().toISOString(),
      }),
    },
  );

  // Typed log message with level override
  log.now(result, { level: LoggerLevel.Debug, tag: 'debugSnapshot' });
}

main().catch((e) => {
  appLogger.error('Unhandled exception', { error: e instanceof Error ? e.message : String(e) });
});
  1. Dependency Injection: composable factories and constants
import 'reflect-metadata';
import { PureContainer } from '@worktif/utils';

// Example services
class ConfigService {
  constructor(public readonly env: 'local' | 'prod' = 'local') {}
}
class MetricsService {
  constructor(public readonly cfg: ConfigService) {}
}

const container = new PureContainer<string>();

// Constants
container.tieConst({
  Env: { instance: null, args: [{ value: process.env.STAGE ?? 'local' }], dependencies: [] },
});

// Factories (note the explicit dependency graph)
container.tie({
  ConfigService: {
    instance: ConfigService,
    args: [], // optional args first
    dependencies: ['Env'], // then dependencies by key
  },
  MetricsService: {
    instance: MetricsService,
    args: [],
    dependencies: ['MetricsService'],
  },
  AnalysisService: {
    instance: AnalysisService,
    args: [{
      value: 'John Doe',
      condition: (v: string) => v === 'John Doe', // optional condition
    }],
    dependencies: ['ConfigService', 'MetricsService'],
  },
});

// Resolve instances
const cfg = container.run<ConfigService>('ConfigService');
const metrics = container.run<MetricsService>('MetricsService');
console.log({ env: cfg.env, hasMetrics: !!metrics });
  1. Decorators: run logic before a method with safe error interception
import 'reflect-metadata';
import { injectBefore, logger, initLog, LoggerLevel } from '@worktif/utils';

type BeforeInstance = { typeDef: 'before_instance'; log: Awaited<ReturnType<typeof initLog>> };

const appLogger = logger({ serviceName: 'orders/api' });

async function beforeStep(...args: any[]): Promise<BeforeInstance> {
  const log = await initLog(appLogger, 'CreateOrder', LoggerLevel.Info);
  log.now(args, { tag: 'methodArgs' });
  return { typeDef: 'before_instance', log };
}

function onCatch(e: any, beforeInstance?: BeforeInstance) {
  const err = e instanceof Error ? e : new Error(String(e));
  beforeInstance?.log.now({
    message: err.message,
    level: LoggerLevel.Error,
    tag: 'exception',
  });
}

class OrderService {
  @injectBefore(beforeStep, onCatch)
  async create(input: { id: string; amount: number }, beforeInstance?: BeforeInstance): Promise<{ ok: boolean }> {
    beforeInstance?.log.now({ step: 'validating' }, { level: LoggerLevel.Debug });
    if (!input.id) throw new Error('Missing id');
    
    // do work...

    beforeInstance?.log.now({ step: 'persisted' }, { tag: 'audit' });
    return { ok: true };
  }
}

(async () => {
  const svc = new OrderService();
  await svc.create({ id: 'o-1', amount: 42 });
})();

API Reference

Logger

  • logger(config?): Logger

    • config.serviceName?: string – appended to base service name to namespace logs
    • config.logFormatter?: LogFormatter – custom powertools-compatible formatter
    • Returns a configured Logger instance with stage-aware defaults:
      • sampleRateValue: 1 in non-prod, 0.1 in prod
      • logLevel: DEBUG in non-prod, INFO in prod
  • initLog(loggerInstance, actionName, logLevel?): Promise<LoggerInstance>

    • loggerInstance: Logger
    • actionName: string – logical operation name for correlation
    • logLevel?: LoggerLevel – default Info
    • Returns { now, future }
      • now(payload, options?): payload | LogItemMessage
        • options.level?: LoggerLevel
        • options.tag?: string – places payload under a structured field
        • options.params?.serializer?: <T>(message: LogItemMessage) => T – transforms final message
        • options.serializer?: EntitySerializer | Promise<EntitySerializer>
          • EntitySerializer: (payload: any) => any – runs before logging
      • future(promise, options?): Promise<payload | LogItemMessage>
        • options.serializer can be async-producing – EntityLoggerSerializer
  • LoggerLevel

    • Debug | Info | Warn | Error | Critical

DI – PureContainer

  • tie(options, ...args): void

    • Registers factories under names. Each factory:
      • instance: new-able constructor
      • args: optional argument descriptors: { value, condition? }[]
        • resolved as the first constructor arguments
      • dependencies: string[]
        • resolved instances are appended as subsequent constructor arguments
    • Deterministic factory resolution with explicit errors for invalid graphs
  • run(name, ...args): T

    • Resolves and instantiates a factory each time (non-singleton by design)
  • runConstant(name): T

    • Retrieves constant value
  • tieConst(options, ...args): void

    • Binds immutable constants under names. Each option accepts:
      • args: [{ value: any, condition?: (v) => any }]
      • dependencies: string[] (not used for constants; for structure compatibility)
    • Example: Env => 'prod', FeatureFlags => { a: true }

Decorators

  • injectBefore(injectFn, injectCatchFn?): MethodDecorator
    • injectFn: (...args) => Promise<BeforeInstance>
      • If BeforeInstance is found among args it is merged; otherwise appended
    • injectCatchFn: (error, ...args) => any
    • Ensures method receives a before-instance context; catches and reports errors

Exceptions

  • CustomException (subset)
    • CustomException.InternalError(message, meta?)
    • Throw typed errors for DI wiring or runtime faults with consistent messages

Common types

  • Maybe<T> = T | undefined
  • Other default informatics types

Serialization helpers

  • loggerSerializers: EntityLoggerSerializerMap
    • axios: extracts response.data for convenient logging

Types

  • EntitySerializer: (payload: any) => any
  • EntityLoggerSerializer: (logger: Logger | Console) => Promise<EntitySerializer>
  • LoggerInstance: { now: (...), future: (...) }
LoggerInstanceOptions: {
  level?: LoggerLevel;
  tag?: string;
  params?: { serializer?: <T>(message: LogItemMessage) => T };
  serializer?: EntitySerializer | Promise<EntitySerializer>;
  }

Use Cases

  • High-volume Lambda handlers
    • Leverage sampling and level-aware logs to reduce noise in prod while preserving debug fidelity in lower stages
    • Async serializers to shape third-party responses (e.g., axios, GraphQL) before logging
  • Regulated workloads (finance/health)
    • Explicit DI wiring yields predictable dependency graphs and repeatable construction paths
    • Structured logs with service/action names aid traceability and audit trails
  • CLI and batch processors
    • ANSI-aware messages, stderr vs stdout separation, and deterministic exit handling
  • Microservices with shared modules
    • Consistent exception and decorator patterns across services enable uniform observability and error surfaces

Design Principles

  • Functional core, imperative shell
    • Serializers and formatters are pure; side effects contained in logger emission
  • Composability over inheritance
    • DI factories and constants as first-class primitives
  • Observability-first
    • Structured events, stage-driven defaults, minimal branching per hot path
  • Zero/low overhead in hot paths
    • Level checks and identity fallbacks keep logging overhead negligible when disabled
  • Explicit wiring
    • Dependency graphs must be declared, preventing hidden coupling

Technical Notes & Conventions

  • All terminal output supports ANSI coloring and explicit no-color scenarios for portable CLI integration.
  • Compatible with both ES module and CommonJS (main points to bundled dist file; types to declaration).
  • Designed for cross-platform shell and cloud (primary focus: Unix-like environments).

Contributing

This section is intended for external publishers responsible for releasing the package to npm. Follow the sequence precisely to ensure auditability, semantic versioning integrity, and a clean release trail.

  • Authenticate to the scoped registry
    • npm login --scope=@worktif
    • If you encounter a TLS/registry error, set the registry explicitly:
      • npm config set registry https://registry.npmjs.org/
  • Complete your enhancement
    • Implement and locally validate your changes (types, build, docs as applicable).
  • Open a Pull Request (PR)
    • Submit your changes for review.
    • Await approval before proceeding.
  • Merge the PR
    • After approval, merge into main using your standard merge policy.
  • Synchronize your local main
    • git checkout main
    • git pull to ensure you’re up to date.
  • Prepare a release branch
    • Create a branch using the release template:
      • releases/v[your.semantic.version-[pre+[meta]]]-next-release-description
  • Bump the version
    • Update the package version according to SemVer (major/minor/patch).
  • Commit the version bump to the release branch
    • Commit only the version change (and any generated artifacts if required by your policy).
  • Push the release branch
    • Push the branch to the remote to trigger any CI gates.
  • Open a Release PR
    • Create a PR from the release branch to main.
    • Await approval and required checks.
  • Merge the Release PR
    • Merge into main after approvals and passing checks.
  • Final synchronization
    • Pull the latest changes from main locally.
  • Validate the version in package.json
    • Ensure the version reflects the intended release.
  • Publish
    • If the version was not increased (npm will reject):
      • Bump the version, commit, and then run yarn run publish:npm.
    • If the version has been increased and publishing fails unexpectedly:
      • Contact the maintainer at [email protected] with context (command output, Node/npm versions, CI logs).

Successful publish output resembles:

+ @worktif/utils@[your.semantic.version-[pre+[meta]]]
✨  Done in 28.81s.

Security and responsible disclosure

  • Do not include secrets in tests or examples
  • Report vulnerabilities privately to the maintainers contact below

License

This project is licensed under the Elastic License 2.0.

  • See LICENSE for the full license text.
  • See NOTICE for attribution and relicensing details (re-licensed from BUSL-1.1 on 2025-09-15).
  • See THIRD_PARTY_LICENSES.txt for third-party attributions and license texts.

Maintainers / Contact

  • Maintainer: Raman Marozau, [email protected]
  • Documentation and support: docs/ generated via TypeDoc

If you have production questions, provide:

  • Package version, Node version, runtime (local/Lambda/container)
  • Minimal reproduction (if applicable)
  • Redacted logs with service name and action name for correlation