# @vvlad1973/utils

TypeScript utility library with various helpers for common programming tasks.

## Installation

```bash
npm install @vvlad1973/utils
```

## Features

- **TypeScript First**: Written entirely in TypeScript with full type definitions
- **ESM Support**: Modern ES modules
- **Well Tested**: 84% code coverage, 100% function coverage
- **Comprehensive**: 8 utility modules covering async operations, datetime, files, JSON, objects, strings, numbers, and miscellaneous utilities

## Modules

### Async Utilities

```typescript
import { filterAsync, mapAsync, replaceAsync } from '@vvlad1973/utils';

// Filter array asynchronously
const filtered = await filterAsync([1, 2, 3, 4], async (n) => n > 2);
// [3, 4]

// Map array asynchronously
const mapped = await mapAsync([1, 2, 3], async (n) => n * 2);
// [2, 4, 6]

// Replace with async function
const text = await replaceAsync('hello world', /\w+/g, async (match) => match.toUpperCase());
// 'HELLO WORLD'
```

### DateTime Utilities

Powered by Luxon for robust datetime operations.

```typescript
import {
  calcWorkdays,
  calcWorkdaysWithHolidays,
  addWorkdays,
  isWorkday,
  isWorkTime,
  toLuxonDateTime,
  TimeZones
} from '@vvlad1973/utils';

// Calculate workdays between dates
const days = calcWorkdays('2023-01-02', '2023-01-13');

// Calculate workdays excluding holidays
const holidays = [{ from: '2023-01-01', to: '2023-01-08' }];
const workdays = calcWorkdaysWithHolidays('2023-01-01', '2023-01-31', {}, holidays);

// Add workdays to a date
const futureDate = addWorkdays('2023-01-02', 10);

// Check if it's a workday
const isWork = isWorkday('2023-01-02'); // true (Monday)

// Check if current time is within work hours
const isDuringWork = isWorkTime(DateTime.now(), { from: '9:00', to: '18:00' });

// Russian timezone shortcuts
console.log(TimeZones.MSK); // 'Europe/Moscow'
```

### File System Utilities

```typescript
import { isFileExists, getAllFilePaths, getFullPath } from '@vvlad1973/utils';

// Check if file exists
const exists = await isFileExists('./package.json');

// Get all files matching pattern
const files = getAllFilePaths('./src', '*.ts', true);

// Get full path from module
const path = getFullPath(import.meta.url, '../data/config.json');
```

### JSON Utilities

```typescript
import { j, jt, js } from '@vvlad1973/utils';

const obj = { name: 'John', age: 30 };

// Compact JSON
console.log(j(obj));
// {"name":"John","age":30}

// Tab-indented JSON
console.log(jt(obj));
// {
//     "name": "John",
//     "age": 30
// }

// Space-indented JSON
console.log(js(obj));
// {
//  "name": "John",
//  "age": 30
// }
```

### Numbers Utilities

```typescript
import { getRandomInt } from '@vvlad1973/utils';

// Generate random integer
const random = getRandomInt(1, 10); // Random number between 1 and 10 (inclusive)
```

### Objects Utilities

```typescript
import {
  clone,
  getValueByPath,
  setValueByPath,
  resolvePath,
  isPrimitive,
  isPromise,
  getRandomValue
} from '@vvlad1973/utils';

// Deep clone object
const cloned = clone({ name: 'John', data: { age: 30 } });

// Get nested value by path
const value = await getValueByPath({ user: { name: 'John' } }, 'user.name');
// 'John'

// Set nested value by path
setValueByPath(obj, 'user.name', 'Jane');

// Check if primitive
isPrimitive(42); // true
isPrimitive({}); // false

// Check if promise
isPromise(Promise.resolve(1)); // true

// Get random value matching pattern
const greetings = { HELLO_1: 'Hi', HELLO_2: 'Hello', OTHER: 'Other' };
const random = getRandomValue('HELLO_\\d', greetings);
// Returns either 'Hi' or 'Hello'
```

### Strings Utilities

```typescript
import {
  isTextMatch,
  getRandomString,
  matchesAnyRegExp,
  countPlaceholders,
  f,
  fillTemplate
} from '@vvlad1973/utils';

// Match text with regex
isTextMatch('test', 'Te.*', true, 'i'); // true

// Generate random string
const random = getRandomString(10); // 10-character alphanumeric string
const digits = getRandomString(6, '0123456789'); // 6-digit number

// Match against multiple regexes
matchesAnyRegExp('test@example.com', [/\w+@\w+\.\w+/, /\d+/]); // true

// Format strings (similar to util.format)
f('My name is %s, I am %d years old', 'John', 30);
// 'My name is John, I am 30 years old'

// Fill template with context
const context = { name: 'Alice', age: 30 };
await fillTemplate('Hello {{name}}, you are {{age}} years old', context);
// 'Hello Alice, you are 30 years old'

await fillTemplate('@NAME is @AGE', { NAME: 'Bob', AGE: '25' });
// 'Bob is 25'
```

### Miscellaneous Utilities

```typescript
import {
  log,
  error,
  delay,
  getCallerName,
  checkEnumValue,
  getConfig,
  getConfigItem,
  envSubst
} from '@vvlad1973/utils';

// Logging
log('Information message');
error('Error message');

// Delay execution
await delay(1000); // Wait 1 second

// Get caller function name
function myFunction() {
  const caller = getCallerName();
  console.log(caller); // 'myFunction'
}

// Check enum value
enum Status { Active = 'active', Inactive = 'inactive' }
checkEnumValue('active', Status); // true

// Load configuration from standard files
const config = await getConfig('./'); // Searches for config.json, config.yml, etc.

// Get config item with environment variable support
getConfigItem({ env: 'DATABASE_URL' }, 'localhost');

// Environment variable substitution (bash-like syntax)
process.env.DATABASE_HOST = 'db.example.com';
envSubst('postgresql://${DATABASE_HOST}:5432/mydb');
// 'postgresql://db.example.com:5432/mydb'

// Use default values
envSubst('Server: ${SERVER_HOST:-localhost}:${SERVER_PORT:-8080}');
// 'Server: localhost:8080' (if variables not set)

// Set defaults, throw errors, conditional values
envSubst('${PORT:=3000}'); // Set PORT to 3000 if not set
envSubst('${REQUIRED:?is required}'); // Throw error if REQUIRED not set
envSubst('Debug ${DEBUG:+enabled}'); // 'Debug enabled' if DEBUG is set
```

#### Configuration with Environment Variables

The `getConfig` function automatically processes configuration values:

```typescript
// config.json
{
  "database": {
    "host": "${DATABASE_HOST:-localhost}",
    "port": "${DATABASE_PORT:-5432}",
    "name": { "env": "DATABASE_NAME" }
  },
  "server": {
    "url": "http://${SERVER_HOST}:${SERVER_PORT:-8080}"
  }
}

// Load config with automatic environment variable substitution
process.env.DATABASE_HOST = 'db.example.com';
process.env.DATABASE_NAME = 'production';
process.env.SERVER_HOST = '192.168.1.1';

const config = await getConfig('./');
// {
//   database: {
//     host: 'db.example.com',
//     port: '5432',
//     name: 'production'
//   },
//   server: {
//     url: 'http://192.168.1.1:8080'
//   }
// }
```

#### envSubst Operators

Supports all bash-like parameter expansion operators:

- `${VAR}` - substitute variable or empty string
- `${VAR:-default}` - use default if unset or empty
- `${VAR-default}` - use default if unset (empty string is kept)
- `${VAR:=default}` - set and use default if unset or empty
- `${VAR=default}` - set and use default if unset
- `${VAR:?error}` - throw error if unset or empty
- `${VAR?error}` - throw error if unset
- `${VAR:+alternate}` - use alternate if set and non-empty
- `${VAR+alternate}` - use alternate if set (even if empty)

## API Documentation

Full API documentation is available via TypeDoc. Run:

```bash
npm run docs
```

## Testing

```bash
# Run tests
npm test

# Run tests in watch mode
npm run test:watch

# Run tests with coverage
npm run test:coverage
```

## Building

```bash
npm run build
```

## License

MIT with Commercial Use

## Author

Vlad Vnukovskiy <vvlad1973@gmail.com>

## Repository

[https://github.com/vvlad1973/utilities](https://github.com/vvlad1973/utilities)
