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
287 lines
7.1 KiB
TypeScript
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('&');
|
|
};
|