workspace-v1/core/modules/utils/string.util.ts
rckrdmrd 66161b1566 feat: Workspace-v1 complete migration with NEXUS v3.4
Sistema NEXUS v3.4 migrado con:

Estructura principal:
- core/orchestration: Sistema SIMCO + CAPVED (27 directivas, 28 perfiles)
- core/catalog: Catalogo de funcionalidades reutilizables
- shared/knowledge-base: Base de conocimiento compartida
- devtools/scripts: Herramientas de desarrollo
- control-plane/registries: Control de servicios y CI/CD
- orchestration/: Configuracion de orquestacion de agentes

Proyectos incluidos (11):
- gamilit (submodule -> GitHub)
- trading-platform (OrbiquanTIA)
- erp-suite con 5 verticales:
  - erp-core, construccion, vidrio-templado
  - mecanicas-diesel, retail, clinicas
- betting-analytics
- inmobiliaria-analytics
- platform_marketing_content
- pos-micro, erp-basico

Configuracion:
- .gitignore completo para Node.js/Python/Docker
- gamilit como submodule (git@github.com:rckrdmrd/gamilit-workspace.git)
- Sistema de puertos estandarizado (3005-3199)

Generated with NEXUS v3.4 Migration System
EPIC-010: Configuracion Git y Repositorios
2026-01-04 03:37:42 -06:00

287 lines
7.1 KiB
TypeScript

/**
* String Utilities - Core Module
*
* Framework-agnostic string manipulation and formatting functions.
* Can be used in any project (NestJS, Express, Frontend, etc.)
*
* @module @core/utils/string
* @version 1.0.0
*/
/**
* Generate slug from string
* @example slugify("Hello World!") => "hello-world"
*/
export const slugify = (text: string): string => {
return text
.toString()
.toLowerCase()
.trim()
.replace(/\s+/g, '-')
.replace(/[^\w-]+/g, '')
.replace(/--+/g, '-')
.replace(/^-+/, '')
.replace(/-+$/, '');
};
/**
* Capitalize first letter
* @example capitalize("hello") => "Hello"
*/
export const capitalize = (text: string): string => {
if (!text) return '';
return text.charAt(0).toUpperCase() + text.slice(1).toLowerCase();
};
/**
* Capitalize each word
* @example capitalizeWords("hello world") => "Hello World"
*/
export const capitalizeWords = (text: string): string => {
return text
.split(' ')
.map((word) => capitalize(word))
.join(' ');
};
/**
* Truncate string with ellipsis
* @example truncate("Hello World", 8) => "Hello..."
*/
export const truncate = (text: string, maxLength: number): string => {
if (text.length <= maxLength) return text;
return text.substring(0, maxLength - 3) + '...';
};
/**
* Remove HTML tags
* @example stripHtml("<p>Hello</p>") => "Hello"
*/
export const stripHtml = (html: string): string => {
return html.replace(/<[^>]*>/g, '');
};
/**
* Sanitize string (remove special chars except spaces)
*/
export const sanitize = (text: string): string => {
return text.replace(/[^\w\s]/gi, '');
};
/**
* Check if string is empty or whitespace
*/
export const isEmpty = (text: string | null | undefined): boolean => {
return !text || text.trim().length === 0;
};
/**
* Check if string is not empty
*/
export const isNotEmpty = (text: string | null | undefined): boolean => {
return !isEmpty(text);
};
/**
* Generate random string
* @param length - Length of string (default: 10)
* @param charset - Character set to use (default: alphanumeric)
*/
export const randomString = (
length: number = 10,
charset: string = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789',
): string => {
let result = '';
for (let i = 0; i < length; i++) {
result += charset.charAt(Math.floor(Math.random() * charset.length));
}
return result;
};
/**
* Generate random numeric string
*/
export const randomNumeric = (length: number = 6): string => {
return randomString(length, '0123456789');
};
/**
* Mask sensitive data (show only last N chars)
* @example maskString("1234567890", 4) => "******7890"
*/
export const maskString = (text: string, visibleChars: number = 4): string => {
if (text.length <= visibleChars) return text;
const masked = '*'.repeat(text.length - visibleChars);
return masked + text.slice(-visibleChars);
};
/**
* Mask email (show first 2 chars and domain)
* @example maskEmail("john@example.com") => "jo***@example.com"
*/
export const maskEmail = (email: string): string => {
const [local, domain] = email.split('@');
if (!domain) return email;
const maskedLocal = local.substring(0, 2) + '***';
return `${maskedLocal}@${domain}`;
};
/**
* Convert string to camelCase
* @example toCamelCase("hello world") => "helloWorld"
*/
export const toCamelCase = (text: string): string => {
return text
.replace(/(?:^\w|[A-Z]|\b\w)/g, (word, index) =>
index === 0 ? word.toLowerCase() : word.toUpperCase(),
)
.replace(/\s+/g, '');
};
/**
* Convert string to snake_case
* @example toSnakeCase("helloWorld") => "hello_world"
*/
export const toSnakeCase = (text: string): string => {
return text
.replace(/([A-Z])/g, '_$1')
.toLowerCase()
.replace(/^_/, '')
.replace(/\s+/g, '_');
};
/**
* Convert string to kebab-case
* @example toKebabCase("helloWorld") => "hello-world"
*/
export const toKebabCase = (text: string): string => {
return text
.replace(/([A-Z])/g, '-$1')
.toLowerCase()
.replace(/^-/, '')
.replace(/\s+/g, '-');
};
/**
* Convert string to CONSTANT_CASE
* @example toConstantCase("helloWorld") => "HELLO_WORLD"
*/
export const toConstantCase = (text: string): string => {
return toSnakeCase(text).toUpperCase();
};
/**
* Convert string to PascalCase
* @example toPascalCase("hello world") => "HelloWorld"
*/
export const toPascalCase = (text: string): string => {
return text
.replace(/(?:^\w|[A-Z]|\b\w)/g, (word) => word.toUpperCase())
.replace(/\s+/g, '');
};
/**
* Escape regex special characters
*/
export const escapeRegex = (text: string): string => {
return text.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
};
/**
* Count words in string
*/
export const wordCount = (text: string): number => {
if (isEmpty(text)) return 0;
return text.trim().split(/\s+/).length;
};
/**
* Reverse string
*/
export const reverse = (text: string): string => {
return text.split('').reverse().join('');
};
/**
* Pad string on left
* @example padLeft("5", 3, "0") => "005"
*/
export const padLeft = (text: string, length: number, char: string = ' '): string => {
return text.padStart(length, char);
};
/**
* Pad string on right
* @example padRight("5", 3, "0") => "500"
*/
export const padRight = (text: string, length: number, char: string = ' '): string => {
return text.padEnd(length, char);
};
/**
* Remove accents from string
* @example removeAccents("café") => "cafe"
*/
export const removeAccents = (text: string): string => {
return text.normalize('NFD').replace(/[\u0300-\u036f]/g, '');
};
/**
* Extract initials from name
* @example getInitials("John Doe") => "JD"
*/
export const getInitials = (name: string, maxChars: number = 2): string => {
return name
.split(' ')
.map((word) => word.charAt(0).toUpperCase())
.slice(0, maxChars)
.join('');
};
/**
* Format number as currency string
* @example formatCurrency(1234.5, "USD") => "$1,234.50"
*/
export const formatCurrency = (
amount: number,
currency: string = 'USD',
locale: string = 'en-US',
): string => {
return new Intl.NumberFormat(locale, {
style: 'currency',
currency,
}).format(amount);
};
/**
* Format number with thousands separator
* @example formatNumber(1234567) => "1,234,567"
*/
export const formatNumber = (num: number, locale: string = 'en-US'): string => {
return new Intl.NumberFormat(locale).format(num);
};
/**
* Parse query string to object
* @example parseQueryString("?foo=bar&baz=qux") => { foo: "bar", baz: "qux" }
*/
export const parseQueryString = (queryString: string): Record<string, string> => {
const params = new URLSearchParams(queryString);
const result: Record<string, string> = {};
params.forEach((value, key) => {
result[key] = value;
});
return result;
};
/**
* Build query string from object
* @example buildQueryString({ foo: "bar", baz: "qux" }) => "foo=bar&baz=qux"
*/
export const buildQueryString = (params: Record<string, string | number | boolean>): string => {
return Object.entries(params)
.filter(([, value]) => value !== undefined && value !== null)
.map(([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(String(value))}`)
.join('&');
};