@tmlmobilidade/utils
Version:
A collection of utility functions and helpers for the TML Mobilidade Go monorepo, providing common functionality for batching operations, caching, HTTP requests, object manipulation, permissions, and more.
242 lines (169 loc) • 6.78 kB
Markdown
A collection of utility functions and helpers for the TML Mobilidade Go monorepo, providing common functionality for batching operations, caching, HTTP requests, object manipulation, permissions, and more.
```bash
npm install @tmlmobilidade/utils
```
Utilities for processing large datasets in chunks to avoid memory issues and improve performance.
- **`performInChunks`** - Processes large arrays in configurable chunks with async operations
In-memory cache implementation with TTL (time-to-live) support.
- **`Cache`** - A simple key-value cache with automatic expiration
### Generic Utilities
Path-based object manipulation utilities with full TypeScript support.
- **`getValueAtPath`** - Retrieves a value from an object using a dot-notation path
- **`setValueAtPath`** - Sets a value at a dot-notation path, creating intermediate objects/arrays as needed
- **`DotPath`** - TypeScript type for generating all possible paths in an object
- **`PathValue`** - TypeScript type for retrieving the value type at a specific path
### HTTP Utilities
Comprehensive HTTP client utilities with retry logic, error handling, and SWR integration.
- **`fetchData`** - Fetches data with automatic retry, exponential backoff, and error handling
- **`multipartFetch`** - Sends multipart form data requests
- **`uploadFile`** - Uploads files using multipart form data
- **`swrFetcher`** - SWR-compatible fetcher with authentication
- **`standardSwrFetcher`** - SWR-compatible fetcher without authentication
- **`HttpResponse`** - Response wrapper with data, error, and status code
- **`WithPagination`** - Type helper for paginated responses
### Object Manipulation
Utilities for comparing, converting, flattening, and merging objects.
- **`compareObjects`** - Deep comparison of objects, returning differences
- **`convertObject`** - Converts objects to match a Zod schema shape
- **`flattenObject`** - Flattens nested objects using dot notation
- **`mergeObjects`** - Deep merges objects and arrays with deduplication
### Permissions
Permission checking utilities for role-based access control.
- **`getPermission`** - Retrieves a permission from a list by scope and action
- **`hasPermission`** - Checks if a permission exists in a list
- **`hasPermissionResource`** - Checks if a value exists in a permission's resource
- **`hasAPIResourcePermission`** - Fastify-specific permission check for API resources
### Singleton Proxy
Utility for creating proxies around async singleton classes.
- **`asyncSingletonProxy`** - Creates a proxy that delays method access until singleton instance is initialized
### Query Validation
Utilities for validating and parsing query parameters.
- **`validateQueryParams`** - Validates query parameters against a Zod schema
## Usage Examples
### Batching
```typescript
import { performInChunks } from '@tmlmobilidade/utils';
const largeDataset = Array.from({ length: 10000 }, (_, i) => ({ id: i }));
await performInChunks(largeDataset, async (chunk) => {
await processChunk(chunk);
}, 1000); // Process 1000 items at a time
```
```typescript
import { Cache } from '@tmlmobilidade/utils';
const cache = new Cache<string, User>(60000); // 60 second TTL
cache.set('user:123', user);
const user = cache.get('user:123');
cache.delete('user:123');
```
```typescript
import { getValueAtPath, setValueAtPath } from '@tmlmobilidade/utils';
const obj = { user: { profile: { name: 'John' } } };
// Get nested value
const name = getValueAtPath(obj, 'user.profile.name'); // 'John'
// Set nested value (creates intermediate objects if needed)
setValueAtPath(obj, 'user.profile.email', 'john@example.com');
```
```typescript
import { fetchData, uploadFile, swrFetcher } from '@tmlmobilidade/utils';
// GET request with retry
const response = await fetchData<User>('/api/users/123');
if (response.isOk()) {
console.log(response.data);
}
// POST request
const createResponse = await fetchData<User>(
'/api/users',
'POST',
{ name: 'John', email: 'john@example.com' }
);
// File upload
const uploadResponse = await uploadFile<{ url: string }>('/api/upload', file);
// SWR integration
const data = await swrFetcher<User[]>('/api/users');
```
```typescript
import { compareObjects, flattenObject, mergeObjects, convertObject } from '@tmlmobilidade/utils';
import { z } from 'zod';
// Compare objects
const prev = { name: 'Alice', age: 30 };
const curr = { name: 'Alice', age: 31 };
const diff = compareObjects(prev, curr);
// { age: { curr_value: 31, prev_value: 30 } }
// Flatten object
const nested = { a: { b: 1, c: { d: 2 } } };
const flat = flattenObject(nested);
// { 'a': { b: 1, c: { d: 2 } }, 'a.b': 1, 'a.c': { d: 2 }, 'a.c.d': 2 }
// Merge objects
const merged = mergeObjects({ a: 1, b: 2 }, { b: 3, c: 4 });
// { a: 1, b: 3, c: 4 }
// Convert to schema
const schema = z.object({ name: z.string(), age: z.number() });
const converted = convertObject({ name: 'John', age: 30, extra: 'ignored' }, schema);
// { name: 'John', age: 30 }
```
```typescript
import { hasPermission, hasPermissionResource } from '@tmlmobilidade/utils';
const permissions = [
{ scope: 'users', action: 'read', resource: { userId: ['123', '456'] } },
{ scope: 'users', action: 'write', resource: { userId: ['*'] } }
];
// Check if permission exists
if (hasPermission(permissions, 'users', 'read')) {
// User has read permission
}
// Check resource-specific permission
if (hasPermissionResource({
permissions,
scope: 'users',
action: 'read',
resource_key: 'userId',
value: '123'
})) {
// User has permission for resource '123'
}
```
```typescript
import { validateQueryParams } from '@tmlmobilidade/utils';
import { z } from 'zod';
const schema = z.object({
page: z.coerce.number().min(1),
limit: z.coerce.number().min(1).max(100)
});
// In a Fastify route handler
const params = validateQueryParams(req.query, schema);
// Throws HttpException if validation fails
```
```typescript
import { asyncSingletonProxy } from '@tmlmobilidade/utils';
class Database {
private static instance: Database | null = null;
static async getInstance(): Promise<Database> {
if (!this.instance) {
this.instance = await this.initialize();
}
return this.instance;
}
async query(sql: string) {
// ...
}
}
// Create proxy that handles async initialization
const db = asyncSingletonProxy(Database);
await db.query('SELECT * FROM users'); // Automatically waits for initialization
```
AGPL-3.0-or-later
[](https://github.com/tmlmobilidade/go)