@ccas-produits-numeriques/common-utilities
v2.1.2
Published
[SIJ] Common utilities shared between applications
Readme
@ccas-produits-numeriques/common-utilities
Universal utilities library for SIJ applications with conditional exports for browser and Node.js environments.
🌟 Features
- Universal: Works in both browser and Node.js environments
- Conditional Exports: Automatically serves the right version based on environment
- Modular: Import only what you need
- Zero Breaking Changes: Seamless integration with existing projects
- TypeScript: Full type definitions included
Installation
Requirements
- Node.js >= 24
- pnpm (recommended package manager)
pnpm add @ccas-produits-numeriques/common-utilities📁 File Structure
src/
├── core/ # Universal utilities (browser + Node.js)
│ ├── assertUnreachable.ts # TypeScript exhaustiveness checking
│ ├── asyncUtils.ts # timeout(), sleep() with AbortSignal
│ ├── diacritics.ts # removeDiacritics() for text processing
│ ├── fixtureHelper.ts # getFixtureValue() for testing
│ ├── generateUri.ts # URI generation with parameters
│ ├── intervals.utils.ts # Date interval operations
│ ├── routes.ts # Route schemas (universal)
│ └── sortAlphabetically.ts # Alphabetical sorting utilities
│
├── http/ # Universal HTTP utilities
│ └── schemas/
│ └── errorResponse.ts # HTTP error response schemas
│
├── types/ # Universal type definitions
│ ├── environment.ts # Environment enums
│ └── errors.ts # Error type definitions
│
├── zod/ # Universal Zod utilities
│ └── date.ts # Date validation and parsing
│
├── logging/ # Universal logging utilities (Node.js-specific)
│ ├── logger.ts # Pino logger with MongoDB support
│ ├── sentry.ts # Sentry error tracking
│ ├── sentry.fastify.ts # Sentry Fastify integration
│ └── withCause.ts # Error causation tracking
│
├── node/ # Node.js-specific utilities
│ ├── core/
│ │ ├── cryptoUtils.ts # Password hashing, key generation
│ │ └── esmUtils.ts # __dirname() for ES modules
│ ├── http/
│ │ ├── apiUtils.ts # File operations, rate limiting
│ │ └── fetchClient.ts # Fetch wrapper with proxy support
│ ├── logging/
│ │ ├── logger.ts # Pino logger with MongoDB support
│ │ ├── sentry.ts # Sentry error tracking
│ │ ├── sentry.fastify.ts # Sentry Fastify integration
│ │ └── withCause.ts # Error causation tracking
│ ├── fastify/
│ │ └── middlewares/
│ │ ├── errorMiddleware.ts # Error handling middleware
│ │ └── logMiddleware.ts # Request logging middleware
│ ├── mongodb/ # MongoDB utilities
│ │ ├── utils.ts # Collection operations
│ │ ├── schema.ts # Schema validation with Zod
│ │ └── indexes.ts # Index management
│ └── types/
│ └── models.ts # MongoDB model descriptors
│
└── browser/ # Browser-specific exports
├── core/ # Re-exports universal core utilities
├── http/ # Re-exports universal HTTP utilities
├── types/ # Re-exports universal types
├── zod/ # Re-exports universal Zod utilities
├── logging/ # Empty (Node.js-only)
└── fastify/ # Empty (Node.js-only)🚀 Quick Start
Universal Utilities (Browser + Node.js)
// String and text processing
import { removeDiacritics, sortAlphabeticallyBy } from "@ccas-produits-numeriques/common-utilities/core";
// Async utilities with AbortSignal support
import { timeout, sleep } from "@ccas-produits-numeriques/common-utilities/core";
// URI generation with type safety
import { generateUri } from "@ccas-produits-numeriques/common-utilities/core";
// TypeScript exhaustiveness checking
import { assertUnreachable } from "@ccas-produits-numeriques/common-utilities/core";
// ESM module utilities (Node.js only)
import { __dirname } from "@ccas-produits-numeriques/common-utilities";
// Route schemas (works in browser and server)
import { zCoreRoutes } from "@ccas-produits-numeriques/common-utilities/core";HTTP Utilities (Universal)
// HTTP error response schemas
import { IResError, IResErrorJson, ZResError } from "@ccas-produits-numeriques/common-utilities/http";
import { zResBadRequest, zResNotFound } from "@ccas-produits-numeriques/common-utilities/http";Type Definitions (Universal)
// Environment types
import { AppEnvironmentEnum } from "@ccas-produits-numeriques/common-utilities/types";
// Error types
import type { IResError, IResErrorJson } from "@ccas-produits-numeriques/common-utilities/types";Node.js-Only Utilities
// Crypto operations (Node.js only)
import { hashPassword, generateKey, verifyPassword } from "@ccas-produits-numeriques/common-utilities";
// File operations and rate limiting (Node.js only)
import { apiRateLimiter, downloadFileAsStream } from "@ccas-produits-numeriques/common-utilities";
// HTTP client with proxy support (Node.js only)
import { getClientFetch, type FetchOptions, type FetchRequestInit } from "@ccas-produits-numeriques/common-utilities";
// Logging with Sentry integration (Node.js only)
import {
createLogger,
initSentry,
withCause,
initSentryFastify,
} from "@ccas-produits-numeriques/common-utilities/logging";
// Fastify middleware (Node.js only)
import { errorMiddleware, logMiddleware } from "@ccas-produits-numeriques/common-utilities/fastify";
// MongoDB utilities (Node.js only)
import {
clearCollection,
clearAllCollections,
collectionExistInDb,
createCollectionIfNotExists,
configureSchemaValidation,
createIndexes,
} from "@ccas-produits-numeriques/common-utilities/mongodb";🔄 How Conditional Exports Work
This library automatically serves the correct version based on your environment:
// Same import, different behavior:
import { assertUnreachable } from "@ccas-produits-numeriques/common-utilities/core";
// In Browser: Gets universal version (src/core/assertUnreachable.ts)
// In Node.js: Gets full version including Node.js-specific utilitiesImport Paths Guide
Universal utilities (Browser + Node.js):
/core- Core utilities including:- String utilities:
removeDiacritics,sortAlphabeticallyBy - Async utilities:
timeout,sleep - URI utilities:
generateUri,generatePath,generateQueryString - Validation:
assertUnreachable - Testing:
getFixtureValue - Intervals:
substractInterval,substractIntervals
- String utilities:
/http- HTTP error schemas only (ZResError,IResError, etc.)/types- Type definitions (AppEnvironmentEnum, etc.)/zod- Zod date utilities (ParisDate,zParisLocalDateString, etc.)
Node.js-specific utilities:
- From root (
@ccas-produits-numeriques/common-utilities) - Node.js-specific utilities:getClientFetch- HTTP client with proxy supportapiRateLimiter- API rate limitingdownloadFileAsStream,downloadFileAsTmp- File operationscomputeFileHash- File hashing__dirname- ESM module directory helperhashPassword,generateKey,verifyPassword- Crypto utilities- Plus all exports from
/core,/http,/types,/zod
/logging- Logging utilities (Node.js only)/fastify- Fastify middleware (Node.js only)
Browser Environment
- ✅ Gets universal utilities only
- ❌ No Node.js dependencies (crypto, fs, async_hooks, etc.)
- ✅ Prevents "Module not found: Can't resolve 'async_hooks'" errors
Node.js Environment
- ✅ Gets all utilities (universal + Node.js-specific)
- ✅ Full crypto, file system, and Sentry integration
- ✅ Fastify middleware and logging capabilities
📦 Dependencies
Always Available
- @hapi/boom: Error handling utilities (included as dependency)
Peer Dependencies (install as needed)
- zod (>= 3.25.64): Schema validation (for
/zodand route schemas) - luxon: Date handling (for
/zoddate utilities) - type-fest: Type utilities (for advanced TypeScript types)
- fastify: Web framework (for
/fastifymiddleware) - pino: Logging (for
/loggingutilities) - @sentry/node: Error tracking (for
/loggingutilities) - mongodb: Database (for model descriptors in
/typesand MongoDB utilities) - zod-mongodb-schema: Zod to MongoDB schema conversion (for MongoDB utilities)
- rate-limiter-flexible: API rate limiting (for
/httputilities)
Installation Examples
# Basic usage (universal utilities only)
pnpm add @ccas-produits-numeriques/common-utilities
# With HTTP error schemas and validation
pnpm add @ccas-produits-numeriques/common-utilities zod
# With date utilities and timezone handling
pnpm add @ccas-produits-numeriques/common-utilities zod luxon
# Node.js server with Fastify integration
pnpm add @ccas-produits-numeriques/common-utilities fastify zod pino @sentry/node
# Full installation (all features)
pnpm add @ccas-produits-numeriques/common-utilities zod luxon fastify pino @sentry/node mongodb zod-mongodb-schema rate-limiter-flexible type-festAPI Documentation
Core Utilities
String Utilities
removeDiacritics(str: string): string- Remove diacritical marks from stringssortAlphabeticallyBy<T>(key: string, array: T[]): T[]- Sort arrays alphabetically by property
URI Utilities
generateUri(path: string, options?: PathOptions): string- Generate URIs with parametersgeneratePath(path: string, params: Record<string, string>): string- Generate paths with parametersgenerateQueryString(query: Record<string, string | string[]>): string- Generate query strings
Validation Utilities
assertUnreachable(x: never): never- TypeScript exhaustiveness checking
Async Utilities
timeout<T>(promise: Promise<T>, millis: number): Promise<T>- Add timeout to promisessleep(ms: number, signal?: AbortSignal): Promise<void>- Abortable sleep function
Crypto Utilities
hashPassword(password: string, rounds: number, salt?: string): string- PBKDF2 password hashinggenerateKey(size: number, format: string): string- Generate secure API keys
Interval Utilities
substractInterval(a: Interval, b: Interval): Interval[]- Subtract date intervalssubstractIntervals(a: Interval[], b: Interval[]): Interval[]- Subtract multiple intervals
ESM Utilities
__dirname(filePath: string): string- Get directory name from import.meta.url in ESM modules
import { __dirname } from "@ccas-produits-numeriques/common-utilities";
// Usage in an ESM module
const currentDir = __dirname(import.meta.url);
console.log(currentDir); // e.g., "/Users/username/project/src"
// Use it to resolve paths relative to the current file
import { join } from "path";
const configPath = join(__dirname(import.meta.url), "../config.json");Types
Environment Types
AppEnvironmentEnum- Environment enumeration (local, recette, production, preview, test)Environment- Type alias for AppEnvironmentEnum
Error Types
ZResError- Zod schema for standardized error responsesIResError- TypeScript interface for error responses
Routes
Route schemas are available universally from the /core module for use in both browser and Node.js environments.
Route Schema Types
IHealthcheckRoutesDef- Interface for healthcheck route definitionszCoreRoutes- Core route schemas including healthcheck endpoint
Zod Utilities
Date Classes
ParisDate- Date class with Paris timezone JSON serializationparisTimezoneDate(parts: DateParts): ParisDate- Create Paris timezone date from parts
Date Parsers
parseParisLocalDate(date: string, time?: string, offset?: number): ParisDate- Parse local datesparseNullableParisLocalDate(date: string | null, time?: string, offset?: number): ParisDate | null- Parse nullable dates
Zod Schemas
zParisLocalDateString- Validate DD/MM/YYYY format and convert to ParisDatezLocalDate- Transform Date to ParisDatezParisLocalDate- Parse ISO date strings to ParisDate
HTTP Utilities
Fetch Client (Node.js only)
getClientFetch(options: FetchOptions): FetchWithParams- Create a fetch client with proxy support
import { getClientFetch } from "@ccas-produits-numeriques/common-utilities";
// Create a configured fetch client
const client = getClientFetch({
baseURL: "https://api.example.com",
headers: {
Accept: "application/json",
"User-Agent": "MyApp/1.0",
},
proxy: "http://proxy.example.com:8080",
env: "production",
timeout: 10000, // 10 seconds
});
// Make requests with params support
const response = await client("/users", {
method: "GET",
params: {
page: 1,
limit: 20,
},
});
// The client is a wrapped fetch function with additional features:
// - Automatic proxy configuration based on environment
// - Timeout support with AbortSignal
// - Base URL support for relative paths
// - Query parameters as an object
// - Default headersFetchOptions
baseURL?: string- Base URL for relative pathsheaders?: Record<string, string>- Default headers for all requestsproxy?: string- Proxy URL (only used in non-local/test environments)env?: string- Environment name (local, test, production, etc.)timeout?: number- Request timeout in milliseconds (default: 5000)
FetchRequestInit
Extends the standard RequestInit with:
params?: Record<string, string | number | boolean>- Query parameters as an object
Rate Limiting
apiRateLimiter(name: string, options: RateLimitOptions): RateLimiterFn- Advanced API rate limiting
File Operations
downloadFileAsTmp(stream: Readable, filename: string, env: string): Promise<string>- Download to temp filedownloadFileAsStream(stream: Readable, filename: string, env: string): Promise<ReadStream>- Download as streamcomputeFileHash(stream: Readable): Promise<string>- Compute SHA-256 hash
Logging Utilities
Logger Creation
createLogger(config: LogConfig): Logger- Create configured Pino loggercreateJobProcessorLogger(logger: PinoLogger): ILogger- Create job processor logger adapter
Error Handling
withCause<T>(error: T, cause: Error, level?: string): T- Add error causation
Sentry Integration
initSentry(config: SentryConfig): void- Initialize Sentry error trackinginitSentryFastify(app: FastifyInstance): void- Initialize Sentry Fastify integrationcloseSentry(): Promise<void>- Graceful Sentry shutdown
Fastify Utilities
Middleware
errorMiddleware(server: FastifyInstance, config: Config): void- Standardized error handlinglogMiddleware(config: Config): FastifyLoggerOptions- Request/response logging with security filtering
MongoDB Utilities (Node.js only)
Collection Operations
clearCollection(collection: Collection): Promise<void>- Clear all documents from a collectionclearAllCollections(db: Db): Promise<void>- Clear all collections in a databasecollectionExistInDb(collections: CollectionInfo[], name: string): boolean- Check if collection existscreateCollectionIfNotExists(db: Db, collectionName: string): Promise<void>- Create collection if it doesn't exist
import { clearCollection, collectionExistInDb } from "@ccas-produits-numeriques/common-utilities/mongodb";
// Clear a single collection
await clearCollection(db.collection("users"));
// Check if collection exists
const collections = await db.listCollections().toArray();
if (collectionExistInDb(collections, "products")) {
console.log("Products collection exists");
}Schema Validation
configureSchemaValidation(db: Db, modelDescriptors: ModelDescriptor[], options: SchemaValidationOptions): Promise<void>- Configure Zod-based schema validation for MongoDB collections
import { configureSchemaValidation } from "@ccas-produits-numeriques/common-utilities/mongodb";
import { z } from "zod";
const userSchema = z.object({
name: z.string(),
email: z.string().email(),
age: z.number().positive(),
});
const modelDescriptors = [
{ collectionName: "users", zod: userSchema },
{ collectionName: "products" }, // No schema validation
];
await configureSchemaValidation(db, modelDescriptors, {
logger,
validationLevel: "strict",
validationAction: "error",
onError: (error) => console.error("Schema validation error:", error),
});Index Management
createIndexes(db: Db, indexDefinitions: IndexDefinition[], options: CreateIndexesOptions): Promise<void>- Create and manage indexes with conflict resolution
import { createIndexes } from "@ccas-produits-numeriques/common-utilities/mongodb";
const indexDefinitions = [
{
collectionName: "users",
indexes: [
[{ email: 1 }, { unique: true, name: "email_unique" }],
[{ name: 1, createdAt: -1 }, { name: "name_date_compound" }],
],
},
{
collectionName: "products",
indexes: [
[{ sku: 1 }, { unique: true }],
[{ category: 1, price: -1 }, { name: "category_price" }],
],
},
];
// Create indexes (with automatic conflict resolution)
await createIndexes(db, indexDefinitions, {
logger,
drop: false, // Set to true to drop all indexes first
onError: (error) => captureException(error),
});
// The function handles:
// - Creating new indexes
// - Resolving conflicts (error codes 85, 86)
// - Dropping outdated indexes
// - Skipping collections without indexesDevelopment
Scripts
# Build the library
pnpm build
# Watch mode for development
pnpm dev
# Run tests
pnpm test
# Run tests in CI mode
pnpm test:ci
# Run tests with coverage
pnpm test:ci:coverage
# Run linting
pnpm lint
# Fix linting issues
pnpm lint:fix
# Format code
pnpm prettier:fix
# Check formatting
pnpm prettier:check
# Type checking
pnpm typecheck
# Run all checks (typecheck, lint, test)
pnpm check
# Clean build directory
pnpm cleanMigration from Shared Package
If you're migrating from a shared package, update your imports:
// Before (shared package)
import { AppEnvironmentEnum } from "shared/types";
import { IRouteSchema } from "shared/routes/common.routes";
import { ZResError } from "shared/src/types/errors";
import { apiRateLimiter } from "./utils/apiUtils";
import { createLogger } from "./services/logger";
import { errorMiddleware } from "./middlewares/errorMiddleware";
// After (@ccas-produits-numeriques/common-utilities)
import { AppEnvironmentEnum } from "@ccas-produits-numeriques/common-utilities/types";
import { zCoreRoutes, IHealthcheckRoutesDef } from "@ccas-produits-numeriques/common-utilities/core";
import { ZResError } from "@ccas-produits-numeriques/common-utilities/types";
import { apiRateLimiter } from "@ccas-produits-numeriques/common-utilities";
import { createLogger } from "@ccas-produits-numeriques/common-utilities/logging";
import { errorMiddleware } from "@ccas-produits-numeriques/common-utilities/fastify";Recent Updates
v1.5.0 - MongoDB Utilities
- Added comprehensive MongoDB utilities for common operations
- Features:
- Collection operations:
clearCollection,clearAllCollections,collectionExistInDb,createCollectionIfNotExists - Schema validation:
configureSchemaValidationwith Zod integration - Index management:
createIndexeswith automatic conflict resolution - Bug fix: Fixed early exit issue in
createIndexes(usingcontinueinstead ofreturn)
- Collection operations:
- Dependencies: Added
zod-mongodb-schemaas peer dependency for schema conversion
v1.4.0 - Native Fetch Client
- Added
getClientFetch()- A lightweight fetch wrapper with proxy support - Features:
- Native Node.js fetch (no axios dependency)
- Automatic proxy configuration via undici's ProxyAgent
- Built-in timeout support with AbortSignal
- Base URL support for relative paths
- Query parameters as objects
- Default headers configuration
- Removed deprecated HTTP clients (
createHttpClient,createHttpClientNative) - Removed
node-fetch-nativedependency in favor of native Node.js fetch
v1.1.0 - Enhanced Logging and Middleware
- Added
initSentryFastify()for Fastify-specific Sentry integration - Enhanced
logMiddleware()with security-aware field filtering - Improved error handling with better causation tracking
- Updated API rate limiter to support both Axios and Fetch clients
- Added comprehensive file operations utilities
- Enhanced crypto utilities with configurable hash rounds
Migration Examples
Migrating from Axios to Fetch Client
// Before: Using axios
import axios from "axios";
const client = axios.create({
baseURL: "https://api.example.com",
headers: { "User-Agent": "MyApp/1.0" },
timeout: 10000,
httpsAgent: new HttpsProxyAgent(config.proxy.https),
});
const response = await client.get("/users", {
params: { page: 1, limit: 20 },
});
const data = response.data;
// After: Using getClientFetch
import { getClientFetch } from "@ccas-produits-numeriques/common-utilities";
const client = getClientFetch({
baseURL: "https://api.example.com",
headers: { "User-Agent": "MyApp/1.0" },
timeout: 10000,
proxy: config.proxy.https,
env: config.env,
});
const response = await client("/users", {
method: "GET",
params: { page: 1, limit: 20 },
});
const data = await response.json();Tools Project Migration
// Before: Local duplicate utilities
import { apiRateLimiter } from "./utils/apiUtils";
import { timeout } from "./utils/asyncUtils";
import { createLogger } from "./services/logger";
import { errorMiddleware } from "./middlewares/errorMiddleware";
import { assertUnreachable } from "shared/src/utils/assertUnreachable";
// After: Direct imports from common-utilities
import { apiRateLimiter } from "@ccas-produits-numeriques/common-utilities";
import { timeout } from "@ccas-produits-numeriques/common-utilities/core";
import { createLogger } from "@ccas-produits-numeriques/common-utilities/logging";
import { errorMiddleware } from "@ccas-produits-numeriques/common-utilities/fastify";
import { assertUnreachable } from "@ccas-produits-numeriques/common-utilities/core";Benefits of Migration
- ✅ Zero code duplication - All utilities centralized in common-utilities
- ✅ Consistent behavior - Same implementation across all projects
- ✅ Better maintenance - Updates propagate from single source
- ✅ Type safety - Full TypeScript support with proper exports
- ✅ Performance - Tree-shakable imports, only load what you need
