open-index/open-npm huggingface dataset:
npm package: @lowerdeck/anonymize-ip
Description: Mask IP addresses for privacy compliance (GDPR, CCPA). Supports both IPv4 and IPv6 with configurable masking levels.
Author: Tobias Herber
Latest version: 1.0.4

# `@lowerdeck/anonymize-ip`

Mask IP addresses for privacy compliance (GDPR, CCPA). Supports both IPv4 and IPv6 with configurable masking levels.

## Installation

```bash
npm install @lowerdeck/anonymize-ip
yarn add @lowerdeck/anonymize-ip
bun add @lowerdeck/anonymize-ip
pnpm add @lowerdeck/anonymize-ip
```

## Usage

```typescript
import { anonymizeIP } from '@lowerdeck/anonymize-ip';

// Default anonymization (keeps 2 groups)
anonymizeIP('192.168.1.1');
// '192.168.x.x'

// Custom keep groups (IPv4: 1-3, IPv6: 1-7)
anonymizeIP('192.168.1.1', { keepGroups: 3 });
// '192.168.1.x'

anonymizeIP('192.168.1.1', { keepGroups: 1 });
// '192.x.x.x'

// Custom mask character
anonymizeIP('192.168.1.1', { maskChar: '0' });
// '192.168.0.0'

// IPv6 support
anonymizeIP('2001:0db8:85a3:0000:0000:8a2e:0370:7334');
// '2001:0db8:x:x:x:x:x:x'

anonymizeIP('2001:0db8:85a3::8a2e:0370:7334', { keepGroups: 3 });
// '2001:0db8:85a3:x:x:x:x:x'
```

## License

This project is licensed under the Apache License 2.0.

<div align="center">
  <sub>Built with ❤️ by <a href="https://metorial.com">Metorial</a></sub>
</div>
npm package: @lowerdeck/api-key
Description: Create and parse structured API keys with embedded configuration. Supports versioning, type prefixes, and automatic validation for secure key management.
Author: Tobias Herber
Latest version: 1.0.2

# `@lowerdeck/api-key`

Create and parse structured API keys with embedded configuration. Supports versioning, type prefixes, and automatic validation for secure key management.

## Installation

```bash
npm install @lowerdeck/api-key
yarn add @lowerdeck/api-key
bun add @lowerdeck/api-key
pnpm add @lowerdeck/api-key
```

## Usage

```typescript
import { createApiKeyGenerator } from '@lowerdeck/api-key';

// Define your key types
const keyTypes = {
  uk: 'user_auth_token',
  ak: 'app_access_token',
  sk: 'secret_key'
} as const;

// Create a key generator
const ApiKey = createApiKeyGenerator(keyTypes, {
  prefix: 'myapp',
  secretLength: 60
});

// Create a new API key
const key = ApiKey.create({
  type: 'user_auth_token',
  config: {
    url: 'https://api.example.com'
  }
});

console.log(key.toString()); // myapp_uk_<60-char-secret><encoded-config>v1

// Parse an existing key
const parsed = ApiKey.from('myapp_uk_...');
if (parsed) {
  console.log(parsed.type); // 'user_auth_token'
  console.log(parsed.config.url); // 'https://api.example.com'
}

// Redact keys for logging
const redacted = ApiKey.redact(key);
console.log(redacted); // myapp_uk_abcd...xyz9
```

## License

This project is licensed under the Apache License 2.0.

<div align="center">
  <sub>Built with ❤️ by <a href="https://metorial.com">Metorial</a></sub>
</div>
npm package: @lowerdeck/api-mux
Description: Route multiplexer for API endpoints. Dispatches requests to different services based on domain, path, and HTTP method with support for fallback handlers.
Author: Tobias Herber
Latest version: 1.0.4

# `@lowerdeck/api-mux`

Route multiplexer for API endpoints. Dispatches requests to different services based on domain, path, and HTTP method with support for fallback handlers.

## Installation

```bash
npm install @lowerdeck/api-mux
yarn add @lowerdeck/api-mux
bun add @lowerdeck/api-mux
pnpm add @lowerdeck/api-mux
```

## Usage

```typescript
import { apiMux } from '@lowerdeck/api-mux';

// Define your services
const mux = apiMux([
  {
    domains: ['api.example.com'],
    methods: ['GET', 'POST'],
    endpoint: {
      path: '/users',
      fetch: async (req) => {
        return new Response('Users API');
      }
    }
  },
  {
    methods: ['POST'],
    endpoint: {
      path: '/webhooks',
      exact: true, // Only match exact path
      fetch: async (req) => {
        return new Response('Webhook handler');
      }
    }
  }
], async (req, server) => {
  // Fallback handler for unmatched routes
  return new Response('Not found', { status: 404 });
});

// Use with your HTTP server
const server = Bun.serve({
  fetch: mux
});
```

## License

This project is licensed under the Apache License 2.0.

<div align="center">
  <sub>Built with ❤️ by <a href="https://metorial.com">Metorial</a></sub>
</div>
npm package: @lowerdeck/base62
Description: Base62 encoding and decoding utility for converting strings and byte arrays into URL-safe, compact representations. Useful for shortening IDs, creating readable tokens, or encoding binary data.
Author: Tobias Herber
Latest version: 1.0.4

# `@lowerdeck/base62`

Base62 encoding and decoding utility for converting strings and byte arrays into URL-safe, compact representations. Useful for shortening IDs, creating readable tokens, or encoding binary data.

## Installation

```bash
npm install @lowerdeck/base62
yarn add @lowerdeck/base62
bun add @lowerdeck/base62
pnpm add @lowerdeck/base62
```

## Usage

```typescript
import { base62 } from '@lowerdeck/base62';

// Encode a string
const encoded = base62.encode('hello');
console.log(encoded); // 'Kq6rA'

// Decode back to string
const decoded = base62.decode('Kq6rA');
console.log(decoded); // 'hello'

// Encode raw bytes
const bytes = new Uint8Array([72, 101, 108, 108, 111]);
const encodedBytes = base62.encode(bytes);

// Decode to raw bytes
const rawBytes = base62.decodeRaw(encodedBytes);
```

## License

This project is licensed under the Apache License 2.0.

<div align="center">
  <sub>Built with ❤️ by <a href="https://metorial.com">Metorial</a></sub>
</div>
npm package: @lowerdeck/cache
Description: Multi-layer caching with Redis and in-memory LRU cache. Supports tag-based invalidation, dynamic TTL, and local-only caching for optimal performance.
Author: Tobias Herber
Latest version: 1.0.1

# `@lowerdeck/cache`

Multi-layer caching with Redis and in-memory LRU cache. Supports tag-based invalidation, dynamic TTL, and local-only caching for optimal performance.

## Installation

```bash
npm install @lowerdeck/cache
yarn add @lowerdeck/cache
bun add @lowerdeck/cache
pnpm add @lowerdeck/cache
```

## Usage

### Redis-backed cache

```typescript
import { createCachedFunction } from '@lowerdeck/cache';

const getUserProfile = createCachedFunction({
  name: 'user-profile',
  redisUrl: 'redis://localhost:6379',
  getHash: (userId: string) => userId,
  provider: async (userId, { setTTL }) => {
    const user = await database.getUser(userId);

    // Dynamically adjust TTL based on result
    if (user.isPremium) {
      setTTL(3600); // 1 hour for premium users
    }

    return user;
  },
  ttlSeconds: 300, // default 5 minutes
  getTags: (user, userId) => [`user:${userId}`, `role:${user.role}`]
});

// Fetch user (checks in-memory cache -> Redis -> provider)
const user = await getUserProfile('123');

// Clear specific user cache
await getUserProfile.clear('123');

// Clear all users with a specific role
await getUserProfile.clearByTag('role:admin');

// Wait for cache clear to complete
await getUserProfile.clearAndWait('123');
await getUserProfile.clearByTagAndWait('role:admin');
```

### Local-only cache

```typescript
import { createLocallyCachedFunction } from '@lowerdeck/cache';

const computeHash = createLocallyCachedFunction({
  getHash: (input: string) => input,
  provider: async (input) => {
    return expensiveHashComputation(input);
  },
  ttlSeconds: 60
});

// First call computes the result
const hash1 = await computeHash('data');

// Second call returns cached result
const hash2 = await computeHash('data');

// Clear cache for specific input
await computeHash.clear('data');
await computeHash.clearAndWait('data');
```

## License

This project is licensed under the Apache License 2.0.

<div align="center">
  <sub>Built with ❤️ by <a href="https://metorial.com">Metorial</a></sub>
</div>
npm package: @lowerdeck/canonicalize
Description: Canonicalize JSON according to RFC 8785 standard for deterministic serialization. Ensures consistent JSON representation regardless of key order, essential for signing and hashing.
Author: Tobias Herber
Latest version: 1.0.4

# `@lowerdeck/canonicalize`

Canonicalize JSON according to RFC 8785 standard for deterministic serialization. Ensures consistent JSON representation regardless of key order, essential for signing and hashing.

## Installation

```bash
npm install @lowerdeck/canonicalize
yarn add @lowerdeck/canonicalize
bun add @lowerdeck/canonicalize
pnpm add @lowerdeck/canonicalize
```

## Usage

```typescript
import { canonicalize } from '@lowerdeck/canonicalize';

// Consistent JSON regardless of key order
const obj1 = { b: 2, a: 1 };
const obj2 = { a: 1, b: 2 };

canonicalize(obj1); // '{"a":1,"b":2}'
canonicalize(obj2); // '{"a":1,"b":2}' - same output

// Useful for signing and hashing
import { hash } from '@lowerdeck/hash';

async function signData(data: any) {
  const canonical = canonicalize(data);
  return await hash.sha256(canonical);
}

// Handles special types
canonicalize({
  date: new Date('2024-01-01'),
  regex: /test/g,
  bigint: 123n
});
```

## License

This project is licensed under the Apache License 2.0.

<div align="center">
  <sub>Built with ❤️ by <a href="https://metorial.com">Metorial</a></sub>
</div>
npm package: @lowerdeck/case
Description: String case conversion utilities supporting multiple formats. Convert between camelCase, PascalCase, snake_case, kebab-case, and more.
Author: Tobias Herber
Latest version: 1.0.9

# `@lowerdeck/case`

String case conversion utilities supporting multiple formats. Convert between camelCase, PascalCase, snake_case, kebab-case, and more.

## Installation

```bash
npm install @lowerdeck/case
yarn add @lowerdeck/case
bun add @lowerdeck/case
pnpm add @lowerdeck/case
```

## Usage

```typescript
import { Cases, capitalize } from '@lowerdeck/case';

// Different case conversions
Cases.toCamelCase('hello world');    // 'helloWorld'
Cases.toPascalCase('hello world');   // 'HelloWorld'
Cases.toKebabCase('hello world');    // 'hello-world'
Cases.toSnakeCase('hello world');    // 'hello_world'
Cases.toTitleCase('hello world');    // 'Hello World'
Cases.toSentenceCase('hello world'); // 'Hello world'

// Convenience function for title case
capitalize('hello world'); // 'Hello World'
```

## License

This project is licensed under the Apache License 2.0.

<div align="center">
  <sub>Built with ❤️ by <a href="https://metorial.com">Metorial</a></sub>
</div>
npm package: @lowerdeck/cron
Description: Distributed cron job scheduler using BullMQ and Redis. Schedule recurring tasks with cron expressions that run reliably across multiple instances.
Author: Tobias Herber
Latest version: 1.1.0

# `@lowerdeck/cron`

Distributed cron job scheduler using BullMQ and Redis. Schedule recurring tasks with cron expressions that run reliably across multiple instances.

## Installation

```bash
npm install @lowerdeck/cron
yarn add @lowerdeck/cron
bun add @lowerdeck/cron
pnpm add @lowerdeck/cron
```

## Usage

```typescript
import { createCron } from '@lowerdeck/cron';

// Create a cron job that runs every minute
const cronJob = createCron(
  {
    name: 'cleanup-task',
    cron: '*/1 * * * *', // every minute
    redisUrl: 'redis://localhost:6379'
  },
  async () => {
    console.log('Running cleanup task');
    // Your task logic here
  }
);

// Start the cron job
const processor = await cronJob.start();

// Later, stop the cron job
await processor.close();

// Run every day at midnight
const dailyJob = createCron(
  {
    name: 'daily-report',
    cron: '0 0 * * *',
    redisUrl: process.env.REDIS_URL
  },
  async () => {
    await generateDailyReport();
  }
);

// Run every 15 minutes
const frequentJob = createCron(
  {
    name: 'sync-data',
    cron: '*/15 * * * *',
    redisUrl: process.env.REDIS_URL
  },
  async () => {
    await syncDataFromAPI();
  }
);
```

## License

This project is licensed under the Apache License 2.0.

<div align="center">
  <sub>Built with ❤️ by <a href="https://metorial.com">Metorial</a></sub>
</div>
npm package: @lowerdeck/delay
Description: Create a promise that resolves after a specified number of milliseconds. Useful for adding delays, timeouts, or implementing retry logic.
Author: Tobias Herber
Latest version: 1.0.4

# `@lowerdeck/delay`

Create a promise that resolves after a specified number of milliseconds. Useful for adding delays, timeouts, or implementing retry logic.

## Installation

```bash
npm install @lowerdeck/delay
yarn add @lowerdeck/delay
bun add @lowerdeck/delay
pnpm add @lowerdeck/delay
```

## Usage

```typescript
import { delay } from '@lowerdeck/delay';

// Wait for 1 second
await delay(1000);
console.log('1 second has passed');

// Add delay between retries
async function fetchWithRetry(url: string, retries = 3) {
  for (let i = 0; i < retries; i++) {
    try {
      return await fetch(url);
    } catch (error) {
      if (i < retries - 1) {
        await delay(1000 * (i + 1)); // exponential backoff
      }
    }
  }
}

// Throttle operations
async function processItems(items: string[]) {
  for (const item of items) {
    await processItem(item);
    await delay(500); // wait 500ms between items
  }
}
```

## License

This project is licensed under the Apache License 2.0.

<div align="center">
  <sub>Built with ❤️ by <a href="https://metorial.com">Metorial</a></sub>
</div>
npm package: @lowerdeck/emitter
Description: Type-safe event emitter with strong typing. Define your event types upfront and get full TypeScript support for event names and payloads.
Author: Tobias Herber
Latest version: 1.0.4

# `@lowerdeck/emitter`

Type-safe event emitter with strong typing. Define your event types upfront and get full TypeScript support for event names and payloads.

## Installation

```bash
npm install @lowerdeck/emitter
yarn add @lowerdeck/emitter
bun add @lowerdeck/emitter
pnpm add @lowerdeck/emitter
```

## Usage

```typescript
import { Emitter } from '@lowerdeck/emitter';

// Define event types
type Events = {
  'user:login': { userId: string; timestamp: Date };
  'user:logout': { userId: string };
  'error': Error;
};

const emitter = new Emitter<Events>();

// Subscribe to events (returns unsubscribe function)
const unsubscribe = emitter.on('user:login', (data) => {
  console.log(`User ${data.userId} logged in`);
});

// Emit events
emitter.emit('user:login', {
  userId: '123',
  timestamp: new Date()
});

// One-time listeners
emitter.once('error', (error) => {
  console.error('Error occurred:', error);
});

// Unsubscribe
unsubscribe();

// Clear all listeners
emitter.clear();
```

## License

This project is licensed under the Apache License 2.0.

<div align="center">
  <sub>Built with ❤️ by <a href="https://metorial.com">Metorial</a></sub>
</div>
npm package: @lowerdeck/encryption
Description: Encryption utility for securely encrypting and decrypting secrets using entity-specific passwords. Uses SHA-512 for key derivation and provides a simple API for secret management.
Author: Tobias Herber
Latest version: 1.0.7

# `@lowerdeck/encryption`

Encryption utility for securely encrypting and decrypting secrets using entity-specific passwords. Uses SHA-512 for key derivation and provides a simple API for secret management.

## Installation

```bash
npm install @lowerdeck/encryption
yarn add @lowerdeck/encryption
bun add @lowerdeck/encryption
pnpm add @lowerdeck/encryption
```

## Usage

```typescript
import { Encryption } from '@lowerdeck/encryption';

// Initialize with a master password
const encryption = new Encryption('master-password-123');

// Encrypt a secret for a specific entity
const encrypted = await encryption.encrypt({
  secret: 'my-api-key',
  entityId: 'user_123'
});

// Decrypt the secret
const decrypted = await encryption.decrypt({
  encrypted,
  entityId: 'user_123'
});
console.log(decrypted); // 'my-api-key'

// Different entity IDs produce different encryptions
await encryption.encrypt({
  secret: 'same-secret',
  entityId: 'user_456'
}); // Different encrypted output
```

## License

This project is licensed under the Apache License 2.0.

<div align="center">
  <sub>Built with ❤️ by <a href="https://metorial.com">Metorial</a></sub>
</div>
npm package: @lowerdeck/env
Description: Type-safe environment variable validation. Ensure required environment variables exist and have proper types at application startup.
Author: Tobias Herber
Latest version: 1.0.5

# `@lowerdeck/env`

Type-safe environment variable validation. Ensure required environment variables exist and have proper types at application startup.

## Installation

```bash
npm install @lowerdeck/env
yarn add @lowerdeck/env
bun add @lowerdeck/env
pnpm add @lowerdeck/env
```

## Usage

```typescript
import { createValidatedEnv } from '@lowerdeck/env';
import { string, number, object } from '@lowerdeck/validation';

// Define environment schema
const env = createValidatedEnv(
  object({
    NODE_ENV: string().oneOf(['development', 'production', 'test']),
    PORT: number().min(1).max(65535),
    DATABASE_URL: string().url(),
    API_KEY: string().min(32),
    DEBUG: string().optional()
  })
);

// Use validated environment variables
console.log(env.PORT);         // number type
console.log(env.NODE_ENV);     // 'development' | 'production' | 'test'
console.log(env.DATABASE_URL); // validated URL string

// Throws error if validation fails
// Error: Environment variable PORT is required
// Error: Environment variable DATABASE_URL must be a valid URL
```

### With Default Values

```typescript
import { createValidatedEnv } from '@lowerdeck/env';
import { string, number, object, optional } from '@lowerdeck/validation';

const env = createValidatedEnv(
  object({
    PORT: optional(number().min(1)).default(3000),
    HOST: optional(string()).default('localhost'),
    LOG_LEVEL: optional(string().oneOf(['debug', 'info', 'warn', 'error'])).default('info')
  })
);

console.log(env.PORT); // 3000 if not set
```

## License

This project is licensed under the Apache License 2.0.

<div align="center">
  <sub>Built with ❤️ by <a href="https://metorial.com">Metorial</a></sub>
</div>
npm package: @lowerdeck/error
Description: Type-safe error handling with structured error objects. Create consistent error responses with status codes, error codes, messages, hints, and descriptions.
Author: Tobias Herber
Latest version: 1.2.2

# `@lowerdeck/error`

Type-safe error handling with structured error objects. Create consistent error responses with status codes, error codes, messages, hints, and descriptions.

## Installation

```bash
npm install @lowerdeck/error
yarn add @lowerdeck/error
bun add @lowerdeck/error
pnpm add @lowerdeck/error
```

## Usage

```typescript
import { createError, ServiceError, isServiceError } from '@lowerdeck/error';

// Define error types
const Errors = {
  UserNotFound: new ServiceError(createError({
    statusCode: 404,
    errorCode: 'USER_NOT_FOUND',
    message: 'User not found',
    hint: 'The requested user does not exist',
    description: 'Check the user ID and try again'
  })),

  Unauthorized: new ServiceError(createError({
    statusCode: 401,
    errorCode: 'UNAUTHORIZED',
    message: 'Unauthorized',
    hint: 'You must be logged in to access this resource'
  }))
};

// Throw errors
function getUser(id: string) {
  const user = findUser(id);
  if (!user) {
    throw Errors.UserNotFound();
  }
  return user;
}

// Handle errors
try {
  const user = getUser('123');
} catch (error) {
  if (isServiceError(error)) {
    console.log(error.statusCode); // 404
    console.log(error.data.errorCode); // 'USER_NOT_FOUND'

    // Convert to HTTP response
    return error.toResponse();
  }
}
```

## License

This project is licensed under the Apache License 2.0.

<div align="center">
  <sub>Built with ❤️ by <a href="https://metorial.com">Metorial</a></sub>
</div>
npm package: @lowerdeck/execution-context
Description: Track and manage execution context for requests, scheduled jobs, and background tasks. Provides type-safe context objects for different execution environments with support for parent-child relationships.
Author: Tobias Herber
Latest version: 1.1.0

# `@lowerdeck/execution-context`

Track and manage execution context for requests, scheduled jobs, and background tasks. Provides type-safe context objects for different execution environments with support for parent-child relationships.

## Installation

```bash
npm install @lowerdeck/execution-context
yarn add @lowerdeck/execution-context
bun add @lowerdeck/execution-context
pnpm add @lowerdeck/execution-context
```

## Usage

```typescript
import { createExecutionContext, ExecutionContext } from '@lowerdeck/execution-context';

// Create context for an HTTP request
const requestContext = createExecutionContext({
  type: 'request',
  userId: 'user_123',
  ip: '192.168.1.1',
  userAgent: 'Mozilla/5.0...'
});

// Create context for a scheduled job
const cronContext = createExecutionContext({
  type: 'scheduled',
  cron: '0 0 * * *',
  name: 'daily-cleanup'
});

// Create context for a queue job
const jobContext = createExecutionContext({
  type: 'job',
  queue: 'emails'
});

// Nest contexts with parent relationships
const childContext = createExecutionContext({
  type: 'job',
  queue: 'notifications',
  parent: requestContext
});

console.log(childContext.contextId); // Auto-generated ID
```

## License

This project is licensed under the Apache License 2.0.

<div align="center">
  <sub>Built with ❤️ by <a href="https://metorial.com">Metorial</a></sub>
</div>
npm package: @lowerdeck/flatten
Description: Flatten nested objects and arrays into single-level key-value pairs using dot notation for objects and bracket notation for arrays.
Author: Tobias Herber
Latest version: 1.0.4

# `@lowerdeck/flatten`

Flatten nested objects and arrays into single-level key-value pairs using dot notation for objects and bracket notation for arrays.

## Installation

```bash
npm install @lowerdeck/flatten
yarn add @lowerdeck/flatten
bun add @lowerdeck/flatten
pnpm add @lowerdeck/flatten
```

## Usage

```typescript
import { flattenObject } from '@lowerdeck/flatten';

// Flatten nested object
const nested = {
  user: {
    name: 'John',
    address: {
      city: 'New York',
      zip: '10001'
    }
  }
};

const flat = flattenObject(nested);
console.log(flat);
// {
//   'user.name': 'John',
//   'user.address.city': 'New York',
//   'user.address.zip': '10001'
// }

// Flatten arrays
const withArray = {
  users: [
    { name: 'Alice' },
    { name: 'Bob' }
  ]
};

const flatArray = flattenObject(withArray);
console.log(flatArray);
// {
//   'users[0].name': 'Alice',
//   'users[1].name': 'Bob'
// }
```

## License

This project is licensed under the Apache License 2.0.

<div align="center">
  <sub>Built with ❤️ by <a href="https://metorial.com">Metorial</a></sub>
</div>
npm package: @lowerdeck/forwarded-for
Description: Extract client IP addresses from various HTTP headers. Handles X-Forwarded-For, X-Real-IP, CF-Connecting-IP, and custom Metorial headers.
Author: Tobias Herber
Latest version: 1.0.4

# `@lowerdeck/forwarded-for`

Extract client IP addresses from various HTTP headers. Handles X-Forwarded-For, X-Real-IP, CF-Connecting-IP, and custom Metorial headers.

## Installation

```bash
npm install @lowerdeck/forwarded-for
yarn add @lowerdeck/forwarded-for
bun add @lowerdeck/forwarded-for
pnpm add @lowerdeck/forwarded-for
```

## Usage

```typescript
import { extractIp, parseForwardedFor } from '@lowerdeck/forwarded-for';

// Extract IP from headers (checks multiple sources)
const ip = extractIp(request.headers);
console.log(ip); // '192.168.1.1'

// Parse X-Forwarded-For header
const ips = parseForwardedFor(request.headers.get('x-forwarded-for'));
console.log(ips); // ['192.168.1.1', '10.0.0.1']

// Checked headers in order:
// 1. metorial-connecting-ip
// 2. cf-connecting-ip (Cloudflare)
// 3. x-forwarded-for (first IP)
// 4. x-real-ip

// Works with plain objects or Headers API
extractIp({
  'x-forwarded-for': '192.168.1.1, 10.0.0.1',
  'x-real-ip': '192.168.1.1'
});

extractIp(new Headers({
  'x-forwarded-for': '192.168.1.1'
}));
```

## License

This project is licensed under the Apache License 2.0.

<div align="center">
  <sub>Built with ❤️ by <a href="https://metorial.com">Metorial</a></sub>
</div>
npm package: @lowerdeck/hash
Description: Cryptographic hash functions using Web Crypto API. Returns base62-encoded hash strings for compact, URL-safe representations.
Author: Tobias Herber
Latest version: 1.0.4

# `@lowerdeck/hash`

Cryptographic hash functions using Web Crypto API. Returns base62-encoded hash strings for compact, URL-safe representations.

## Installation

```bash
npm install @lowerdeck/hash
yarn add @lowerdeck/hash
bun add @lowerdeck/hash
pnpm add @lowerdeck/hash
```

## Usage

```typescript
import { Hash } from '@lowerdeck/hash';

// SHA-256 hash
const hash256 = await Hash.sha256('hello world');
console.log(hash256); // base62-encoded hash

// Different hash algorithms
const hash1 = await Hash.sha1('data');
const hash384 = await Hash.sha384('data');
const hash512 = await Hash.sha512('data');

// Hash user passwords (use dedicated password hashing in production)
async function hashPassword(password: string) {
  return await Hash.sha512(password);
}

// Create content-based IDs
async function createContentHash(content: string) {
  return await Hash.sha256(content);
}
```

## License

This project is licensed under the Apache License 2.0.

<div align="center">
  <sub>Built with ❤️ by <a href="https://metorial.com">Metorial</a></sub>
</div>
npm package: @lowerdeck/hono
Description: Create Hono web applications with Metorial defaults. Includes error handling for ServiceError, 404 handling, and CORS configuration.
Author: Tobias Herber
Latest version: 1.0.9

# `@lowerdeck/hono`

Create Hono web applications with Metorial defaults. Includes error handling for ServiceError, 404 handling, and CORS configuration.

## Installation

```bash
npm install @lowerdeck/hono
yarn add @lowerdeck/hono
bun add @lowerdeck/hono
pnpm add @lowerdeck/hono
```

## Usage

```typescript
import { createHono, cors } from '@lowerdeck/hono';

// Create Hono app with defaults
const app = createHono('/api');

// Routes
app.get('/users/:id', async (c) => {
  const id = c.req.param('id');
  const user = await getUser(id);
  return c.json(user);
});

// ServiceError handling is automatic
import { createError } from '@lowerdeck/error';

const UserNotFound = createError({
  statusCode: 404,
  errorCode: 'USER_NOT_FOUND',
  message: 'User not found'
});

app.get('/users/:id', async (c) => {
  const user = await getUser(id);
  if (!user) {
    throw UserNotFound(); // Automatically handled
  }
  return c.json(user);
});

// Custom CORS configuration
app.use('/api/*', cors({
  origin: 'https://example.com',
  credentials: true
}));

export default app;
```

## License

This project is licensed under the Apache License 2.0.

<div align="center">
  <sub>Built with ❤️ by <a href="https://metorial.com">Metorial</a></sub>
</div>
npm package: @lowerdeck/id
Description: Generate various types of unique IDs with prefixes. Supports sorted IDs with timestamps, unsorted random IDs, key IDs with checksums, and Snowflake-style distributed IDs.
Author: Tobias Herber
Latest version: 1.0.8

# `@lowerdeck/id`

Generate various types of unique IDs with prefixes. Supports sorted IDs with timestamps, unsorted random IDs, key IDs with checksums, and Snowflake-style distributed IDs.

## Installation

```bash
npm install @lowerdeck/id
yarn add @lowerdeck/id
bun add @lowerdeck/id
pnpm add @lowerdeck/id
```

## Usage

### Sorted IDs (with timestamp)

```typescript
import { generateId } from '@lowerdeck/id';

// Generate sorted ID with timestamp prefix
const id = generateId('user', 16);
console.log(id); // 'user_01HQRS9Z3K7M2N5P'

// IDs are sortable by creation time
const id1 = generateId('order');
const id2 = generateId('order');
console.log(id1 < id2); // true
```

### Unsorted Random IDs

```typescript
import { generateCustomId } from '@lowerdeck/id';

// Generate random ID
const id = generateCustomId('session', 24);
console.log(id); // 'session_abc123xyz789'

// Without prefix
const randomId = generateCustomId(undefined, 16);
```

### Plain IDs and Codes

```typescript
import { generatePlainId, generateCode } from '@lowerdeck/id';

// Alphanumeric string
const plainId = generatePlainId(12);
console.log(plainId); // 'aBc123XyZ456'

// Numeric code
const code = generateCode(6);
console.log(code); // '123456'
```

### Snowflake IDs

```typescript
import { generateSnowflakeId } from '@lowerdeck/id';

// Generate distributed ID
const snowflake = generateSnowflakeId('sf');
console.log(snowflake); // 'sf_1234567890123456789'
```

### Type-safe ID Generator

```typescript
import { createIdGenerator } from '@lowerdeck/id';

const ids = createIdGenerator({
  user: 'user',
  order: 'order',
  session: 'session'
});

// Type-safe ID generation
const userId = ids.user(); // 'user_...'
const orderId = ids.order(); // 'order_...'
```

## License

This project is licensed under the Apache License 2.0.

<div align="center">
  <sub>Built with ❤️ by <a href="https://metorial.com">Metorial</a></sub>
</div>
npm package: @lowerdeck/ip-info
Description: Get geolocation information for IP addresses. Retrieves country, city, timezone, coordinates, ASN, and organization data from the geojs.io API.
Author: Tobias Herber
Latest version: 1.0.4

# `@lowerdeck/ip-info`

Get geolocation information for IP addresses. Retrieves country, city, timezone, coordinates, ASN, and organization data from the geojs.io API.

## Installation

```bash
npm install @lowerdeck/ip-info
yarn add @lowerdeck/ip-info
bun add @lowerdeck/ip-info
pnpm add @lowerdeck/ip-info
```

## Usage

```typescript
import { ipInfo } from '@lowerdeck/ip-info';

// Get info for single IP
const info = await ipInfo.get('8.8.8.8');
console.log(info);
// {
//   country: 'US',
//   city: 'Mountain View',
//   timezone: 'America/Los_Angeles',
//   latitude: '37.386',
//   longitude: '-122.0838',
//   asn: 'AS15169',
//   organization: 'Google LLC'
// }

// Get info for multiple IPs
const infos = await ipInfo.getMany(['8.8.8.8', '1.1.1.1']);

// Safe get with error handling
const safeInfo = await ipInfo.getSafe('invalid-ip');
console.log(safeInfo); // null on error
```

## License

This project is licensed under the Apache License 2.0.

<div align="center">
  <sub>Built with ❤️ by <a href="https://metorial.com">Metorial</a></sub>
</div>
npm package: @lowerdeck/join-paths
Description: Join URL path segments and query parameters. Handles path normalization, null/undefined filtering, and automatic query string conversion.
Author: Tobias Herber
Latest version: 1.0.4

# `@lowerdeck/joinPaths`

Join URL path segments and query parameters. Handles path normalization, null/undefined filtering, and automatic query string conversion.

## Installation

```bash
npm install @lowerdeck/joinPaths
yarn add @lowerdeck/joinPaths
bun add @lowerdeck/joinPaths
pnpm add @lowerdeck/joinPaths
```

## Usage

```typescript
import { joinPaths } from '@lowerdeck/joinPaths';

// Join path segments
joinPaths('/api', 'users', '123');
// '/api/users/123'

// Add query parameters
joinPaths('/api', 'users', { page: 1, limit: 10 });
// '/api/users?page=1&limit=10'

// Skip null/undefined values
joinPaths('/api', null, 'users', undefined, '123');
// '/api/users/123'

// Normalize paths
joinPaths('/api/', '/users/', '/123');
// '/api/users/123'

// Mix paths and query params
joinPaths('/api/users', { sort: 'name', order: 'asc' });
// '/api/users?sort=name&order=asc'
```

## License

This project is licensed under the Apache License 2.0.

<div align="center">
  <sub>Built with ❤️ by <a href="https://metorial.com">Metorial</a></sub>
</div>
npm package: @lowerdeck/jwt
Description: JWT signing and verification using the jose library. Supports HS256, RS256, and ES384 algorithms with standard JWT claims.
Author: Tobias Herber
Latest version: 1.0.4

# `@lowerdeck/jwt`

JWT signing and verification using the jose library. Supports HS256, RS256, and ES384 algorithms with standard JWT claims.

## Installation

```bash
npm install @lowerdeck/jwt
yarn add @lowerdeck/jwt
bun add @lowerdeck/jwt
pnpm add @lowerdeck/jwt
```

## Usage

```typescript
import { JWT } from '@lowerdeck/jwt';

// Sign a JWT
const token = await JWT.sign(
  { userId: '123', role: 'admin' },
  {
    issuer: 'myapp',
    audience: 'myapp-users',
    expiresIn: '1h',
    alg: 'HS256'
  },
  'your-secret-key'
);

// Verify and decode
const payload = await JWT.verify(
  token,
  {
    issuer: 'myapp',
    audience: 'myapp-users',
    alg: 'HS256'
  },
  'your-secret-key'
);
console.log(payload); // { userId: '123', role: 'admin', iss: 'myapp', ... }

// Decode without verification
const decoded = JWT.decode(token);
console.log(decoded);

// Use RSA keys
const rsaToken = await JWT.sign(
  data,
  { alg: 'RS256', expiresIn: '24h' },
  privateKeyPEM
);

await JWT.verify(rsaToken, { alg: 'RS256' }, publicKeyPEM);
```

## License

This project is licensed under the Apache License 2.0.

<div align="center">
  <sub>Built with ❤️ by <a href="https://metorial.com">Metorial</a></sub>
</div>
npm package: @lowerdeck/lock
Description: Distributed locking using Redlock for Redis. Ensures atomic operations across multiple instances with automatic lock extension and retry logic.
Author: Tobias Herber
Latest version: 1.0.5

# `@lowerdeck/lock`

Distributed locking using Redlock for Redis. Ensures atomic operations across multiple instances with automatic lock extension and retry logic.

## Installation

```bash
npm install @lowerdeck/lock
yarn add @lowerdeck/lock
bun add @lowerdeck/lock
pnpm add @lowerdeck/lock
```

## Usage

```typescript
import { createLock } from '@lowerdeck/lock';

const lock = createLock({
  name: 'my-service',
  redisUrl: 'redis://localhost:6379'
});

// Use lock to ensure only one instance runs a critical section
await lock.usingLock('resource-key', async ({ passForNow }) => {
  console.log('Inside critical section');

  // If you want to pass on this execution and retry later
  if (shouldSkip) {
    passForNow();
    return;
  }

  await performCriticalOperation();
});

// Ensure a function runs only once across all instances
await lock.doOnce('unique-task-id', async () => {
  console.log('This will run on only one instance');
  await oneTimeSetup();
});
// Other instances will wait and return null

// Run once and share the result with other instances
const result = await lock.doOnceAndReturn('compute-key', async () => {
  console.log('Computing expensive result once');
  return await expensiveComputation();
});
// First instance computes and caches, others get cached result

// Lock multiple resources at once
await lock.usingLock(['user:123', 'account:456'], async () => {
  await transferFunds();
});
```

## License

This project is licensed under the Apache License 2.0.

<div align="center">
  <sub>Built with ❤️ by <a href="https://metorial.com">Metorial</a></sub>
</div>
npm package: @lowerdeck/memo
Description: Memoization decorator that caches function results based on arguments. Returns cached results for subsequent calls with the same arguments.
Author: Tobias Herber
Latest version: 1.0.4

# `@lowerdeck/memo`

Memoization decorator that caches function results based on arguments. Returns cached results for subsequent calls with the same arguments.

## Installation

```bash
npm install @lowerdeck/memo
yarn add @lowerdeck/memo
bun add @lowerdeck/memo
pnpm add @lowerdeck/memo
```

## Usage

```typescript
import { memo } from '@lowerdeck/memo';

// Memoize expensive computation
const fibonacci = memo((n: number): number => {
  if (n <= 1) return n;
  return fibonacci(n - 1) + fibonacci(n - 2);
});

fibonacci(40); // computed
fibonacci(40); // returned from cache

// Memoize API calls
const fetchUser = memo(async (id: string) => {
  const response = await fetch(`/api/users/${id}`);
  return response.json();
});

await fetchUser('123'); // makes API call
await fetchUser('123'); // returns cached result
```

## License

This project is licensed under the Apache License 2.0.

<div align="center">
  <sub>Built with ❤️ by <a href="https://metorial.com">Metorial</a></sub>
</div>
npm package: @lowerdeck/merge
Description: Deep merge multiple objects recursively. Nested objects are merged together, while primitive values are overwritten by later sources.
Author: Tobias Herber
Latest version: 1.0.4

# `@lowerdeck/merge`

Deep merge multiple objects recursively. Nested objects are merged together, while primitive values are overwritten by later sources.

## Installation

```bash
npm install @lowerdeck/merge
yarn add @lowerdeck/merge
bun add @lowerdeck/merge
pnpm add @lowerdeck/merge
```

## Usage

```typescript
import { merge } from '@lowerdeck/merge';

// Merge nested objects
const defaults = {
  server: { port: 3000, host: 'localhost' },
  database: { url: 'localhost:5432' }
};

const config = {
  server: { port: 8080 },
  database: { ssl: true }
};

const result = merge({}, defaults, config);
// {
//   server: { port: 8080, host: 'localhost' },
//   database: { url: 'localhost:5432', ssl: true }
// }

// Merge multiple sources
const merged = merge(
  {},
  { a: 1, b: { c: 2 } },
  { b: { d: 3 } },
  { b: { e: 4 } }
);
// { a: 1, b: { c: 2, d: 3, e: 4 } }

// Arrays are replaced, not merged
merge({ arr: [1, 2] }, { arr: [3, 4] }); // { arr: [3, 4] }
```

## License

This project is licensed under the Apache License 2.0.

<div align="center">
  <sub>Built with ❤️ by <a href="https://metorial.com">Metorial</a></sub>
</div>
npm package: @lowerdeck/murmur3
Description: Fast, non-cryptographic hash function implementation of MurmurHash 3.0. Perfect for hash tables, bloom filters, or when you need quick consistent hashing without cryptographic guarantees.
Author: Tobias Herber
Latest version: 1.0.4

# `@lowerdeck/murmur3`

Fast, non-cryptographic hash function implementation of MurmurHash 3.0. Perfect for hash tables, bloom filters, or when you need quick consistent hashing without cryptographic guarantees.

## Installation

```bash
npm install @lowerdeck/murmur3
yarn add @lowerdeck/murmur3
bun add @lowerdeck/murmur3
pnpm add @lowerdeck/murmur3
```

## Usage

```typescript
import { murmur3_32 } from '@lowerdeck/murmur3';

// Basic hashing
const hash = murmur3_32('hello world');
console.log(hash); // 3756498238

// With custom seed for different hash variants
const hash1 = murmur3_32('hello world', 0);
const hash2 = murmur3_32('hello world', 42);
console.log(hash1 !== hash2); // true
```

## License

This project is licensed under the Apache License 2.0.

<div align="center">
  <sub>Built with ❤️ by <a href="https://metorial.com">Metorial</a></sub>
</div>
npm package: @lowerdeck/normalize-email
Description: Normalize email addresses to their canonical form. Handles Gmail dots, plus-addressing, and domain variations to prevent duplicate accounts.
Author: Tobias Herber
Latest version: 1.0.3

# `@lowerdeck/normalize-email`

Normalize email addresses to their canonical form. Handles Gmail dots, plus-addressing, and domain variations to prevent duplicate accounts.

## Installation

```bash
npm install @lowerdeck/normalize-email
yarn add @lowerdeck/normalize-email
bun add @lowerdeck/normalize-email
pnpm add @lowerdeck/normalize-email
```

## Usage

```typescript
import { normalizeEmail } from '@lowerdeck/normalize-email';

// Remove dots in Gmail addresses
normalizeEmail('john.doe@gmail.com');
// => 'johndoe@gmail.com'

// Remove plus-addressing
normalizeEmail('user+tag@example.com');
// => 'user@example.com'

// Handle googlemail.com -> gmail.com
normalizeEmail('user@googlemail.com');
// => 'user@gmail.com'

// Combine all normalization rules
normalizeEmail('John.Doe+newsletter@GmailMail.com');
// => 'johndoe@gmail.com'

// Prevent duplicate accounts
const users = new Map();
function registerUser(email: string) {
  const normalized = normalizeEmail(email);

  if (users.has(normalized)) {
    throw new Error('Account already exists');
  }

  users.set(normalized, { email });
}
```

## License

This project is licensed under the Apache License 2.0.

<div align="center">
  <sub>Built with ❤️ by <a href="https://metorial.com">Metorial</a></sub>
</div>
npm package: @lowerdeck/once
Description: Ensure a function is called only once, returning the cached result on subsequent calls. Useful for initialization functions or expensive operations that should run exactly once.
Author: Tobias Herber
Latest version: 1.0.4

# `@lowerdeck/once`

Ensure a function is called only once, returning the cached result on subsequent calls. Useful for initialization functions or expensive operations that should run exactly once.

## Installation

```bash
npm install @lowerdeck/once
yarn add @lowerdeck/once
bun add @lowerdeck/once
pnpm add @lowerdeck/once
```

## Usage

```typescript
import { once } from '@lowerdeck/once';

// Initialize database connection once
const initDatabase = once(() => {
  console.log('Connecting to database...');
  return createConnection();
});

initDatabase(); // logs "Connecting to database..."
initDatabase(); // returns cached connection, no log

// Lazy computation
const getExpensiveValue = once(() => {
  console.log('Computing...');
  return complexCalculation();
});

const value1 = getExpensiveValue(); // logs "Computing..."
const value2 = getExpensiveValue(); // returns cached value
```

## License

This project is licensed under the Apache License 2.0.

<div align="center">
  <sub>Built with ❤️ by <a href="https://metorial.com">Metorial</a></sub>
</div>
npm package: @lowerdeck/pagination
Description: Cursor-based pagination utilities with support for Prisma and custom data providers. Provides type-safe pagination with configurable limits and ordering.
Author: Tobias Herber
Latest version: 1.0.7

# `@lowerdeck/pagination`

Cursor-based pagination utilities with support for Prisma and custom data providers. Provides type-safe pagination with configurable limits and ordering.

## Installation

```bash
npm install @lowerdeck/pagination
yarn add @lowerdeck/pagination
bun add @lowerdeck/pagination
pnpm add @lowerdeck/pagination
```

## Usage

```typescript
import { Paginator } from '@lowerdeck/pagination';

// Create a paginator with a data provider
const usersPaginator = Paginator.create(
  ({ prisma }) => prisma.user.findMany,
  {
    defaultLimit: 20,
    defaultOrder: 'asc'
  }
);

// Fetch paginated results
const page1 = await usersPaginator.run({
  limit: 10,
  order: 'asc'
});

// Navigate using cursors
const page2 = await usersPaginator.run({
  limit: 10,
  after: page1.items[page1.items.length - 1].id
});

console.log(page2.pagination.hasNextPage); // true/false
console.log(page2.pagination.hasPreviousPage); // true/false

// Present paginated data
const presented = await Paginator.presentLight(page1, (user) => ({
  id: user.id,
  name: user.name
}));

console.log(presented); // { object: 'list', items: [...], pagination: {...} }
```

## License

This project is licensed under the Apache License 2.0.

<div align="center">
  <sub>Built with ❤️ by <a href="https://metorial.com">Metorial</a></sub>
</div>
npm package: @lowerdeck/presenter
Description: Type-safe presentation layer for transforming domain objects into API responses. Supports multiple API versions, validation schemas, and context-aware transformations.
Author: Tobias Herber
Latest version: 1.0.2

# `@lowerdeck/presenter`

Type-safe presentation layer for transforming domain objects into API responses. Supports multiple API versions, validation schemas, and context-aware transformations.

## Installation

```bash
npm install @lowerdeck/presenter
yarn add @lowerdeck/presenter
bun add @lowerdeck/presenter
pnpm add @lowerdeck/presenter
```

## Usage

```typescript
import { PresentableType, Presenter, declarePresenter } from '@lowerdeck/presenter';
import { v } from '@lowerdeck/validation';

// Define a presentable type
const UserType = PresentableType.create<{ id: string; name: string }>()('user');

// Create a presenter
const userPresenter = Presenter.create(UserType)
  .presenter(async (user, context) => ({
    object: 'user' as const,
    id: user.id,
    name: user.name,
    email: context.accessType === 'user_auth_token' ? 'hidden' : user.email
  }))
  .schema(v.object({
    object: v.literal('user'),
    id: v.string(),
    name: v.string()
  }))
  .build();

// Present data
const result = userPresenter.present(
  { id: '123', name: 'John' },
  { apiVersion: 'mt_2025_01_01_pulsar', accessType: 'user_auth_token' }
);

const data = await result.run({});
console.log(data); // { object: 'user', id: '123', name: 'John', ... }
```

## License

This project is licensed under the Apache License 2.0.

<div align="center">
  <sub>Built with ❤️ by <a href="https://metorial.com">Metorial</a></sub>
</div>
npm package: @lowerdeck/programmable-promise
Description: Promise with externally controllable resolve and reject. Allows you to create a promise and settle it from outside, useful for complex control flow patterns.
Author: Tobias Herber
Latest version: 1.0.6

# `@lowerdeck/programmable-promise`

Promise with externally controllable resolve and reject. Allows you to create a promise and settle it from outside, useful for complex control flow patterns.

## Installation

```bash
npm install @lowerdeck/programmable-promise
yarn add @lowerdeck/programmable-promise
bun add @lowerdeck/programmable-promise
pnpm add @lowerdeck/programmable-promise
```

## Usage

```typescript
import { ProgrammablePromise } from '@lowerdeck/programmable-promise';

// Create a promise you can control externally
const pp = new ProgrammablePromise<string>();

// Pass the promise around
setTimeout(() => {
  pp.resolve('Hello!');
}, 1000);

const result = await pp.promise;
console.log(result); // 'Hello!'

// Useful for event-driven flows
class EventHandler {
  private completion = new ProgrammablePromise<void>();

  onComplete() {
    return this.completion.promise;
  }

  handleEvent(event: Event) {
    if (event.type === 'done') {
      this.completion.resolve();
    }
  }
}

// Access resolved value without awaiting
const pp2 = new ProgrammablePromise<number>();
pp2.resolve(42);
console.log(pp2.value); // 42
```

## License

This project is licensed under the Apache License 2.0.

<div align="center">
  <sub>Built with ❤️ by <a href="https://metorial.com">Metorial</a></sub>
</div>
npm package: @lowerdeck/proxy
Description: Create a proxy object for method interception and routing. Useful for building RPC-style clients where property access and method calls are captured and forwarded.
Author: Tobias Herber
Latest version: 1.0.4

# `@lowerdeck/proxy`

Create a proxy object for method interception and routing. Useful for building RPC-style clients where property access and method calls are captured and forwarded.

## Installation

```bash
npm install @lowerdeck/proxy
yarn add @lowerdeck/proxy
bun add @lowerdeck/proxy
pnpm add @lowerdeck/proxy
```

## Usage

```typescript
import { proxy } from '@lowerdeck/proxy';

// Create RPC client
interface API {
  users: {
    get(id: string): Promise<User>;
    list(): Promise<User[]>;
  };
}

const api = proxy<API>(async (path, ...args) => {
  const method = path.join('.');
  return fetch('/api', {
    method: 'POST',
    body: JSON.stringify({ method, args })
  }).then(r => r.json());
});

// Method calls are captured and routed
await api.users.get('123'); // calls callback with ['users', 'get'] and ['123']
await api.users.list();     // calls callback with ['users', 'list'] and []

// Build type-safe API clients
const client = proxy<typeof serverAPI>(async (path, ...args) => {
  return rpcCall(path.join('.'), args);
});
```

## License

This project is licensed under the Apache License 2.0.

<div align="center">
  <sub>Built with ❤️ by <a href="https://metorial.com">Metorial</a></sub>
</div>