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 🙏

© 2026 – Pkg Stats / Ryan Hefner

@ciq-dev/neoiq-foundation-node

v1.0.1

Published

Node.js observability foundation for CommerceIQ services. Integrates with Groundcover via OpenTelemetry.

Readme

@ciq-dev/neoiq-foundation-node

Node.js observability foundation for CommerceIQ services. Integrates with Groundcover via OpenTelemetry.

┌─────────────────┐     ┌─────────────────┐     ┌─────────────────┐
│   Our Service   │────▶│  OTEL Collector │────▶│   Groundcover   │
│  (auth-service) │     │   (in-cluster)  │     │   (dashboard)   │
└─────────────────┘     └─────────────────┘     └─────────────────┘
        │                        │
        ├── Traces (OTLP) ───────┤
        └── Metrics (OTLP) ──────┘

        │
        └── Logs (stdout) ──────▶ Groundcover Agent scrapes container logs

Features

  • Traces: Automatic span creation for HTTP requests (incoming & outgoing)
  • Metrics: HTTP request counts, durations, errors + custom business metrics
  • Logs: Structured JSON logs with automatic trace context (traceId, spanId, correlationId)
  • Selective Enablement: Enable only the features you need
  • Type-Safe Configuration: Zod validation with helpful error messages
  • Fastify Plugin: One-line integration for request lifecycle
  • HTTP Client: Axios wrapper with retry, circuit breaker, and trace propagation
  • Object Store Adapter: Provider-agnostic wrapper for cloud object storage (e.g. S3) to avoid direct SDK coupling

Quick Start

1. Install

npm install @ciq-dev/neoiq-foundation-node

# Peer dependencies
npm install zod @opentelemetry/api @opentelemetry/sdk-node @opentelemetry/sdk-metrics \
  @opentelemetry/exporter-trace-otlp-grpc @opentelemetry/exporter-metrics-otlp-grpc \
  @opentelemetry/auto-instrumentations-node @opentelemetry/resources @opentelemetry/semantic-conventions \
  pino pino-pretty axios axios-retry opossum fastify-plugin

# Optional (only if you want AWS S3 implementation of ObjectStore)
npm install @aws-sdk/client-s3 @aws-sdk/s3-request-presigner

2. Initialize (First Line of the App)

import { createFoundation } from '@ciq-dev/neoiq-foundation-node';

// Create foundation instance (must be called BEFORE other imports)
const foundation = createFoundation({
  serviceName: 'auth-service',
  serviceVersion: '1.0.0',
});

// Export for use in other files
export { foundation };

3. Add Fastify Plugin

import Fastify from 'fastify';
import { foundation } from './foundation';

const app = Fastify();
app.register(foundation.fastifyPlugin);

4. Set Environment Variable in Kubernetes

env:
  - name: OTEL_EXPORTER_OTLP_ENDPOINT
    value: 'http://otel-stack-deployment-collector.observability.svc.cluster.local:4317'

That's it! Traces and metrics will flow to Groundcover.


Selective Feature Enablement

Enable only the features you need for lightweight deployments:

import { createFoundation } from '@ciq-dev/neoiq-foundation-node';

// Full observability (default)
const foundation = createFoundation({
  serviceName: 'auth-service',
});

// Logging only (lightweight mode)
const foundation = createFoundation({
  serviceName: 'simple-worker',
  features: {
    tracing: false,
    metrics: false,
    logging: true,
  },
});

// Custom auto-instrumentation
const foundation = createFoundation({
  serviceName: 'api-gateway',
  features: {
    tracing: true,
    metrics: true,
    autoInstrumentation: {
      http: true,
      fastify: true,
      mongodb: false,  // Not using MongoDB
      redis: false,    // Not using Redis
      pg: true,        // Using PostgreSQL
    },
  },
});

API Reference

createFoundation(options)

Create a foundation instance with full observability capabilities.

import { createFoundation } from '@ciq-dev/neoiq-foundation-node';

const foundation = createFoundation({
  // Required
  serviceName: 'my-service',
  
  // Optional
  serviceVersion: '1.0.0',        // Default: '1.0.0' or $SERVICE_VERSION
  environment: 'production',       // Default: 'development' or $NODE_ENV
  
  // Feature toggles
  features: {
    tracing: true,                 // Enable distributed tracing
    metrics: true,                 // Enable metrics collection
    logging: true,                 // Enable structured logging
    autoInstrumentation: {         // Fine-grained control
      http: true,
      fastify: true,
      express: true,
      mongodb: true,
      pg: true,
      mysql: true,
      redis: true,
      ioredis: true,
      fs: false,                   // Disabled by default (noisy)
      dns: false,                  // Disabled by default
    },
  },
  
  // OTEL configuration
  otel: {
    endpoint: 'http://...:4317',   // Default: cluster OTEL Collector
    metricsIntervalMs: 5000,       // Default: 5000 (5 seconds)
    traceSampleRate: 1.0,          // Default: 1.0 (100%)
  },
  
  // Logging configuration
  logging: {
    level: 'info',                 // 'debug' | 'info' | 'warn' | 'error'
    prettyPrint: false,            // Auto-detect from environment
  },
});

Foundation Instance

The foundation instance provides access to all observability features:

// Logging
foundation.logger.info({ userId: '123' }, 'User logged in');
foundation.logger.error({ error: err.message }, 'Failed to process');

// Metrics
const meter = foundation.getMeter('my-service');
const counter = meter.createCounter('logins.total');
counter.add(1, { provider: 'workos' });

// Tracing
const tracer = foundation.getTracer();
tracer.startActiveSpan('validateToken', (span) => {
  // ... do work
  span.end();
});

// HTTP Client
const client = foundation.createHttpClient({
  baseURL: 'http://user-service:3000',
  serviceName: 'user-service',
});

// Fastify Plugin
app.register(foundation.fastifyPlugin);

// Shutdown
await foundation.shutdown();

// Check features
console.log(foundation.features);
// { tracing: true, metrics: true, logging: true }

foundation.logger

Structured logger with automatic trace context injection.

foundation.logger.info({ userId: '123' }, 'User logged in');
foundation.logger.error({ error: err.message }, 'Failed to process');
foundation.logger.warn({ attempt: 3 }, 'Retry attempt');
foundation.logger.debug({ payload: data }, 'Processing request');

// Child logger for component-specific logging
const authLogger = foundation.logger.child({ component: 'auth' });
authLogger.info({}, 'Auth module initialized');

Log Output:

{
  "level": "info",
  "time": 1703318400000,
  "service": "auth-service",
  "traceId": "abc123...",
  "spanId": "def456...",
  "correlationId": "req-789",
  "userId": "123",
  "msg": "User logged in"
}

foundation.getMeter(name, version?)

Get a meter for custom metrics.

const meter = foundation.getMeter('auth-service');

// Counter
const counter = meter.createCounter('logins.total');
counter.add(1, { provider: 'workos' });

// Histogram
const histogram = meter.createHistogram('token.validation.duration');
histogram.record(150, { status: 'success' });

// Observable Gauge
meter.createObservableGauge('active_sessions', {}, (result) => {
  result.observe(getActiveSessions());
});

foundation.fastifyPlugin

Fastify plugin for automatic request handling.

app.register(foundation.fastifyPlugin);

Automatically:

  • Extracts/generates correlation ID (x-request-id header)
  • Creates OpenTelemetry spans for each request
  • Logs request received/completed with full context
  • Records HTTP metrics (http.server.requests.total, http.server.request.duration)
  • Skips health check endpoints (/health, /healthz, /ready, /live)

foundation.createHttpClient(options)

Create an Axios client with full observability.

const userClient = foundation.createHttpClient({
  baseURL: 'http://user-service:3000',
  serviceName: 'user-service',
  timeout: 10000,
  retry: {
    retries: 3,
    retryDelay: 1000,
    retryStatusCodes: [408, 429, 500, 502, 503, 504],
  },
  circuitBreaker: {
    enabled: true,
    resetTimeout: 30000,
    errorThresholdPercentage: 50,
  },
});

const response = await userClient.get('/api/users/123');

Automatically:

  • Propagates trace context (traceparent header)
  • Propagates correlation ID (x-request-id header)
  • Logs outbound requests/responses
  • Records HTTP client metrics
  • Retries on 5xx errors with exponential backoff
  • Circuit breaker protection

foundation.shutdown()

Gracefully shutdown OTEL providers. Call on application shutdown.

process.on('SIGTERM', async () => {
  await app.close();
  await foundation.shutdown(); // Flush telemetry
});

Object Store Adapter (S3 / Cloud Abstraction)

Use this when you want to decouple application code from aws-sdk / cloud vendor SDKs.

AWS S3 (optional peer deps)

import { AwsS3ObjectStore } from '@ciq-dev/neoiq-foundation-node';

const store = new AwsS3ObjectStore({
  clientOptions: { region: process.env.AWS_REGION || 'us-east-1' },
});

const ref = { bucket: 'my-bucket', key: 'path/to/file.json' };

// Upload
await store.putObject(ref, JSON.stringify({ ok: true }), { contentType: 'application/json' });

// Presigned PUT URL (browser upload)
const uploadUrl = await store.presignPutObject(ref, { expiresInSeconds: 60, contentType: 'application/json' });

In-memory (local/tests)

import { InMemoryObjectStore } from '@ciq-dev/neoiq-foundation-node';

const store = new InMemoryObjectStore();
await store.putObject({ bucket: 'b', key: 'k' }, 'hello');
const obj = await store.getObject({ bucket: 'b', key: 'k' });

Complete Example

// src/foundation.ts
import { createFoundation } from '@ciq-dev/neoiq-foundation-node';

export const foundation = createFoundation({
  serviceName: 'auth-service',
  environment: process.env.NODE_ENV as 'development' | 'production',
});
// src/index.ts
import Fastify from 'fastify';
import { foundation } from './foundation';

const app = Fastify();

// Register observability plugin
app.register(foundation.fastifyPlugin);

// Create HTTP clients
const userClient = foundation.createHttpClient({
  baseURL: process.env.USER_SERVICE_URL!,
  serviceName: 'user-service',
});

// Custom metrics
const meter = foundation.getMeter('auth-service');
const loginCounter = meter.createCounter('auth.logins.total');

// Routes
app.post('/api/v1/auth/login', async (req, reply) => {
  const { provider } = req.body as { provider: string };
  
  loginCounter.add(1, { provider });
  foundation.logger.info({ provider }, 'Login attempt');
  
  // Call user service (trace context automatically propagated)
  const { data: user } = await userClient.get('/api/users/me');
  
  return { success: true, user };
});

// Start with graceful shutdown
app.listen({ port: 3000 });

process.on('SIGTERM', async () => {
  await app.close();
  await foundation.shutdown();
});

Environment Variables

| Variable | Description | Default | |----------|-------------|---------| | OTEL_EXPORTER_OTLP_ENDPOINT | OTEL Collector URL | http://otel-stack-deployment-collector.observability.svc.cluster.local:4317 | | SERVICE_VERSION | Service version | 1.0.0 | | NODE_ENV | Environment | development | | LOG_LEVEL | Log level | info |


What Gets Sent to Groundcover

Traces

  • Every HTTP request (incoming and outgoing)
  • Correlation ID linking requests across services
  • Span attributes: method, url, status_code, duration

Metrics

  • http.server.requests.total - Incoming request count
  • http.server.request.duration - Incoming request latency
  • http.server.requests.errors - Incoming request errors
  • http.client.requests.total - Outgoing request count
  • http.client.request.duration - Outgoing request latency
  • Custom business metrics you define

Logs (via stdout)

  • Structured JSON logs written to stdout (Pino)
  • Include traceId, spanId, correlationId for correlation
  • Groundcover Agent scrapes container logs
  • Automatically correlated with traces in Groundcover dashboard

Project Structure

src/
├── index.ts                    # Main exports
├── config.ts                   # Zod configuration schemas
├── foundation.ts               # createFoundation() entry point
├── features/
│   ├── context.ts              # AsyncLocalStorage context manager
│   ├── logging.ts              # Pino logger setup
│   ├── tracing.ts              # OTEL trace provider
│   └── metrics.ts              # OTEL meter provider
└── integrations/
    ├── fastify-plugin.ts       # Fastify observability plugin
    ├── http-client.ts          # Axios wrapper with observability
    └── object-store/           # Cloud object store abstraction (S3, etc.)

Development

# Install dependencies
npm install

# Build
npm run build

# Type check
npm run typecheck

# Lint
npm run lint

# Format
npm run prettier:write

License

MIT