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

@acontplus/core

v1.3.2

Published

Enterprise-grade core library implementing Domain-Driven Design (DDD) and Clean Architecture patterns. Provides value objects, use cases, entities, HTTP adapters, pricing calculations, API response handling, and foundational utilities for Angular applicat

Readme

@acontplus/core

Core library for AcontPlus applications, providing essential utilities, domain models, clean architecture patterns, internationalization infrastructure, and business logic components following Domain-Driven Design (DDD) principles.

Installation

npm install @acontplus/core
# or
pnpm add @acontplus/core

Features

  • Internationalization: Language enum (20 languages), BCP47 mapping, browser language detection, localized API error codes and application messages
  • API Response Handling: ApiResponse<T> envelope, ApiError model, functional response helpers (mapSuccess, flatMap, matchResponse)
  • Clean Architecture: Ports and adapters pattern for external integrations
  • Domain Models: Base entities, value objects, and domain-specific models
  • Pricing Engine: Comprehensive pricing calculations with discount, tax, profit, and line item calculators
  • Use Cases: Base use case pattern for business logic encapsulation
  • Value Objects: Money, EntityId, IdentificationNumber, and AuthTokens value objects
  • Environment Configuration: Type-safe environment configuration interfaces
  • Constants: Application constants including SRI document types
  • Type Definitions: Comprehensive TypeScript type definitions for pricing and business logic
  • Environment Configuration: Type-safe environment configuration interfaces
  • Type Definitions: Comprehensive TypeScript type definitions for pricing and business logic

Architecture

This library follows Clean Architecture principles with clear separation of concerns:

  • Enums: Language codes, API error codes, application message keys
  • Ports: Interfaces for external dependencies
  • Models: Domain entities and data transfer objects
  • Helpers: Functional response transformation utilities
  • Value Objects: Immutable objects representing domain concepts
  • Types: TypeScript definitions for type safety

Usage

Internationalization

The library provides a Language enum with 20 languages matching the .NET backend Acontplus.Core.Enums.Language enum, plus browser detection and BCP47 tag mapping.

import {
  Language,
  languageToBcp47,
  bcp47ToLanguage,
  detectBrowserLanguage,
  detectBrowserLanguages,
} from '@acontplus/core';

const lang = detectBrowserLanguage();
console.log(languageToBcp47(lang));
// e.g. 'es' for Spanish (Ecuador), 'en' for English, 'fr' for French

console.log(bcp47ToLanguage('zh-TW'));
// Language.ChineseTraditional (8) — respects zh-cn vs zh-tw regions

const browserLangs = detectBrowserLanguages();
// ['es-EC', 'en-US', 'fr'] — from navigator.languages

Language enum values

| Value | BCP47 | Language | | ----------------------------------- | --------- | ----------------------- | | Language.English (1) | en | English | | Language.Spanish (2) | es | Spanish (Spain) | | Language.SpanishLatinAmerica (20) | es-419 | Spanish (Latin America) | | Language.French (3) | fr | French | | Language.German (4) | de | German | | Language.Italian (5) | it | Italian | | Language.Portuguese (6) | pt | Portuguese | | Language.ChineseSimplified (7) | zh-Hans | Chinese (Simplified) | | Language.ChineseTraditional (8) | zh-Hant | Chinese (Traditional) | | Language.Japanese (9) | ja | Japanese | | Language.Korean (10) | ko | Korean | | Language.Russian (11) | ru | Russian | | Language.Arabic (12) | ar | Arabic | | Language.Dutch (13) | nl | Dutch | | Language.Swedish (14) | sv | Swedish | | Language.Norwegian (15) | nb | Norwegian | | Language.Danish (16) | da | Danish | | Language.Finnish (17) | fi | Finnish | | Language.Polish (18) | pl | Polish | | Language.Hindi (19) | hi | Hindi |

Localized API Error Codes

ApiErrorCode enum maps server error codes to human-readable, translated messages. Supports all 20 languages with fallback to English for unsupported languages.

import {
  ApiErrorCode,
  getLocalizedErrorMessage,
  hasLocalizedMessage,
  Language,
} from '@acontplus/core';

const message = getLocalizedErrorMessage(ApiErrorCode.NOT_FOUND, Language.Spanish);
// 'Recurso no encontrado'

const esMessage = getLocalizedErrorMessage('UNHANDLED_ERROR', Language.Spanish);
// 'Ha ocurrido un error inesperado'

const unknown = getLocalizedErrorMessage('CUSTOM_APP_CODE', Language.Spanish, 'Fallback text');
// 'Fallback text' — returns the fallback when no translation exists

if (hasLocalizedMessage('BAD_REQUEST')) {
  // code has a known translation
}

Supported error codes (matching the .NET ApiExceptionMiddleware):

| Code | English | Spanish | | --------------------- | ------------------------ | -------------------------- | | BAD_REQUEST | Invalid request | Solicitud inválida | | UNAUTHORIZED | Authentication required | Autenticación requerida | | FORBIDDEN | Access denied | Acceso denegado | | NOT_FOUND | Resource not found | Recurso no encontrado | | CONFLICT | Conflict occurred | Conflicto detectado | | METHOD_NOT_ALLOWED | Method not allowed | Método no permitido | | VALIDATION_ERROR | Validation failed | Validación fallida | | RATE_LIMITED | Too many requests | Demasiadas solicitudes | | INTERNAL_ERROR | Internal server error | Error interno del servidor | | SERVICE_UNAVAILABLE | Service unavailable | Servicio no disponible | | TIMEOUT | Request timeout | Tiempo de espera agotado | | NETWORK_ERROR | Network connection error | Error de conexión de red | | UNHANDLED_ERROR | Unexpected error | Error inesperado |

Localized Application Messages

AppMessageKey provides translated UI strings for success notifications, error titles, and common actions. Used by apiInterceptor and NotificationService.

import { AppMessageKey, getLocalizedAppMessage, Language } from '@acontplus/core';

const msg = getLocalizedAppMessage(AppMessageKey.DATA_SAVED, Language.Spanish);
// 'Datos guardados exitosamente'

const title = getLocalizedAppMessage(AppMessageKey.CONNECTION_ERROR, Language.French);
// 'Erreur de connexion'

Available message keys:

| Key | English | Category | | -------------------------- | -------------------------------- | -------- | | OPERATION_COMPLETED | Operation completed successfully | Success | | DATA_SAVED | Data saved successfully | Success | | DATA_UPDATED | Data updated successfully | Success | | ITEM_DELETED | Item deleted successfully | Success | | ITEM_CREATED | Item created successfully | Success | | UPLOADED | File uploaded successfully | Success | | SYNCED | Data synchronized successfully | Success | | FAILED_SAVE | Failed to save data | Error | | FAILED_DELETE | Failed to delete item | Error | | FAILED_UPDATE | Failed to update data | Error | | FAILED_UPLOAD | Failed to upload file | Error | | UNEXPECTED_ERROR | An unexpected error occurred | Error | | CONNECTION_ERROR | Connection Error | Title | | SERVER_ERROR | Server Error | Title | | ERROR | Error | Title | | NETWORK_UNAVAILABLE | Unable to connect to server | Error | | VALIDATION_ERROR_MESSAGE | Validation error | Error | | UNAUTHORIZED_ACCESS | Unauthorized access | Error | | UNSAVED_CHANGES | You have unsaved changes | Warning | | SESSION_EXPIRING | Your session is about to expire | Warning | | LOADING | Loading data... | Info | | PROCESSING | Processing request... | Info |

Pre-built translations exist for: English, Spanish, Spanish (Latin America), French, German, Italian, and Portuguese. Other languages fall back to English.

API Response Handling

Response Envelope

import { ApiResponse, ApiError } from '@acontplus/core';

interface ApiResponse<T> {
  status: 'success' | 'error' | 'warning';
  code: string;
  data?: T;
  message?: string;
  errors?: ApiError[];
  warnings?: ApiError[];
  metadata?: Record<string, unknown>;
  correlationId?: string;
  traceId?: string;
  timestamp: string;
}

interface ApiError {
  code: string;
  message: string;
  target?: string;
  details?: Record<string, unknown>;
  severity?: string;
  category?: string;
  helpUrl?: string;
  suggestedAction?: string;
  traceId?: string;
}

Response Helpers

Functional utilities for transforming ApiResponse<T> envelopes, inspired by the backend Result<TValue, TError> monad and ResultApiExtensions.

import {
  mapSuccess,
  flatMap,
  matchResponse,
  extractData,
  extractErrors,
  firstErrorMessage,
  hasErrorCode,
  toApiError,
  successResponse,
  errorResponse,
  singleErrorResponse,
} from '@acontplus/core';

const response = successResponse(user);
const mapped = mapSuccess(response, user => user.name);
// ApiResponse<string> with data = user.name

const result = flatMap(getUser(1), user => successResponse(user.profile));
// chains two ApiResponse operations

const msg = matchResponse(
  getData(),
  data => `Got ${data}`,
  err => `Failed: ${firstErrorMessage(err)}`,
);

const data = extractData(response);
// T | undefined

const errors = extractErrors(errorResponse);
// ApiError[]

if (hasErrorCode(response, 'NOT_FOUND')) {
  /* ... */
}

Envelope Factories

const success = successResponse({ id: 1 }, 'Created');
// { status: 'success', code: '200', data: { id: 1 }, message: 'Created', ... }

const errors = [toApiError('VAL_001', 'Name is required')];
const failure = errorResponse(errors, '422', 'Validation failed');
// { status: 'error', code: '422', errors: [...], message: 'Validation failed', ... }

const single = singleErrorResponse('NF', 'Not found', '404');
// Convenience for single-error responses

Domain Models

import { BaseEntity, ApiResponse, PagedResult } from '@acontplus/core';

class Customer extends BaseEntity {
  constructor(
    public readonly id: number,
    public readonly name: string,
    public readonly email: string,
  ) {
    super();
  }
}

const paginated: PagedResult<Customer> = {
  items: customers,
  totalCount: 100,
  pageIndex: 1,
  pageSize: 10,
  totalPages: 10,
  hasPreviousPage: false,
  hasNextPage: true,
};

Pricing Calculations

import {
  DiscountCalculator,
  TaxCalculator,
  PricingCalculator,
  ProfitCalculator,
  LineItemCalculator,
} from '@acontplus/core';

const discountCalc = new DiscountCalculator();
const discount = discountCalc.calculate(100, 10);

const taxCalc = new TaxCalculator();
const tax = taxCalc.calculate(100, 0.12);

const profitCalc = new ProfitCalculator();
const profit = profitCalc.calculate(cost, sellingPrice);

const lineItemCalc = new LineItemCalculator();
const lineTotal = lineItemCalc.calculate(quantity, unitPrice, discount, tax);

const pricingCalc = new PricingCalculator();
const finalPrice = pricingCalc.calculateTotal(items, discounts, taxes);

Value Objects

import { MoneyVo, EntityIdVo, IdentificationNumberVo, AuthTokens } from '@acontplus/core';

const price = new MoneyVo(99.99, 'USD');
const discountedPrice = price.subtract(new MoneyVo(10.0, 'USD'));

const customerId = new EntityIdVo('12345');

const ecuadorianId = new IdentificationNumberVo('1234567890');

const tokens = new AuthTokens('access_token', 'refresh_token');

Use Cases

import { UseCase } from '@acontplus/core';
import { Observable } from 'rxjs';

class CreateCustomerUseCase implements UseCase<CreateCustomerRequest, Customer> {
  constructor(private customerRepository: CustomerRepository) {}

  execute(request: CreateCustomerRequest): Observable<Customer> {
    this.validateRequest(request);
    return this.customerRepository.create(request);
  }

  private validateRequest(request: CreateCustomerRequest): void {
    if (!request.name?.trim()) {
      throw new Error('Customer name is required');
    }
  }
}

Constants

import {
  SRI_DOCUMENT_TYPE,
  SRI_DOCUMENT_TYPE_CUSTOM,
  SEPARATOR_KEY_CODE,
  SEPARADORES_REGEX,
} from '@acontplus/core';

const invoiceType = SRI_DOCUMENT_TYPE.FACTURA;
const customType = SRI_DOCUMENT_TYPE_CUSTOM.PROFORMA;
const separatorCode = SEPARATOR_KEY_CODE.DASH;
const separatorPattern = SEPARADORES_REGEX;

Environment Configuration

import { Environment } from '@acontplus/core';

const environment: Environment = {
  apiBaseUrl: 'https://api.example.com',
  isProduction: false,
  tokenKey: 'auth_token',
  refreshTokenKey: 'refresh_token',
  clientId: 'your-client-id',
  loginRoute: 'login',
};