open-index/open-npm huggingface dataset:
npm package: @lowerdeck/queue
Description: Reliable job queue built on BullMQ and Redis. Add jobs to a persistent queue and process them with automatic retries, concurrency control, and graceful shutdown.
Author: Tobias Herber
Latest version: 1.0.8

# `@lowerdeck/queue`

Reliable job queue built on BullMQ and Redis. Add jobs to a persistent queue and process them with automatic retries, concurrency control, and graceful shutdown.

## Installation

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

## Usage

```typescript
import { createQueue, runQueueProcessors } from '@lowerdeck/queue';

// Create a queue
const emailQueue = createQueue<{ to: string; subject: string; body: string }>({
  name: 'email-queue',
  redisUrl: 'redis://localhost:6379',
  workerOpts: {
    concurrency: 10 // process up to 10 jobs concurrently
  }
});

// Add jobs to the queue
await emailQueue.add({
  to: 'user@example.com',
  subject: 'Welcome',
  body: 'Welcome to our service!'
});

// Add multiple jobs at once
await emailQueue.addMany([
  { to: 'user1@example.com', subject: 'Hello', body: 'Message 1' },
  { to: 'user2@example.com', subject: 'Hello', body: 'Message 2' }
]);

// Add job with options
await emailQueue.add(
  { to: 'user@example.com', subject: 'Reminder', body: 'Don\'t forget!' },
  { delay: 60000 } // delay by 1 minute
);

// Process jobs
const processor = emailQueue.process(async (job) => {
  console.log('Sending email to:', job.to);
  await sendEmail(job);
});

// Start the processor with graceful shutdown
await runQueueProcessors([processor]);

// Wait for a job to finish
const jobHandle = await emailQueue.add({ to: 'test@example.com', subject: 'Test', body: 'Test' });
await jobHandle.waitUntilFinished();

// Combine multiple queue processors
import { combineQueueProcessors } from '@lowerdeck/queue';

const combined = combineQueueProcessors([
  emailQueue.process(handleEmail),
  notificationQueue.process(handleNotification)
]);

const { close } = await combined.start();
// Later: await close();
```

## 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/random-from-array
Description: Pick a random element from an array. Returns null if the array is empty.
Author: Tobias Herber
Latest version: 1.0.4

# `@lowerdeck/random-from-array`

Pick a random element from an array. Returns null if the array is empty.

## Installation

```bash
npm install @lowerdeck/random-from-array
yarn add @lowerdeck/random-from-array
bun add @lowerdeck/random-from-array
pnpm add @lowerdeck/random-from-array
```

## Usage

```typescript
import { randomFromArray } from '@lowerdeck/random-from-array';

// Pick a random fruit
const fruit = randomFromArray(['apple', 'banana', 'orange']);
console.log(fruit); // 'banana' (or any random element)

// Empty array returns null
const empty = randomFromArray([]);
console.log(empty); // null

// Works with any type
const user = randomFromArray([
  { id: 1, name: 'Alice' },
  { id: 2, name: 'Bob' },
  { id: 3, name: 'Charlie' }
]);
```

## 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/random-number
Description: Generate random integers within a specified range. Simple wrapper around Math.random() with inclusive bounds.
Author: Tobias Herber
Latest version: 1.0.4

# `@lowerdeck/random-number`

Generate random integers within a specified range. Simple wrapper around Math.random() with inclusive bounds.

## Installation

```bash
npm install @lowerdeck/random-number
yarn add @lowerdeck/random-number
bun add @lowerdeck/random-number
pnpm add @lowerdeck/random-number
```

## Usage

```typescript
import { randomNumber } from '@lowerdeck/random-number';

// Generate random number between 1 and 100 (inclusive)
const num = randomNumber(1, 100);

// Roll a dice
const diceRoll = randomNumber(1, 6);

// Random year in the 2000s
const year = randomNumber(2000, 2099);
```

## 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/redis
Description: Redis client utilities with automatic reconnection, lazy initialization, and Redis Streams support. Simplifies Redis connection management with exponential backoff and error handling.
Author: Tobias Herber
Latest version: 1.0.5

# `@lowerdeck/redis`

Redis client utilities with automatic reconnection, lazy initialization, and Redis Streams support. Simplifies Redis connection management with exponential backoff and error handling.

## Installation

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

## Usage

```typescript
import { createRedisClient, RedisStreams, parseRedisUrl } from '@lowerdeck/redis';

// Create a Redis client with automatic reconnection
const client = createRedisClient({
  redisUrl: 'redis://localhost:6379'
});

// Connect eagerly
const redis = await client.eager();
await redis.set('key', 'value');

// Or use lazy connection (connects on first use)
const lazyRedis = client.lazy();
await lazyRedis().set('key', 'value');

// Parse Redis URLs
const config = parseRedisUrl('redis://user:pass@localhost:6379/2');
// => { host: 'localhost', port: 6379, password: 'pass', db: 2, tls: false }

// Use Redis Streams for event messaging
const streams = new RedisStreams<{ userId: string; action: string }>(
  'user-events',
  'redis://localhost:6379'
);

// Send messages to the stream
await streams.send({
  userId: '123',
  action: 'login'
});

// Create a consumer to process messages
await streams.createReceiver(
  {
    groupId: 'user-event-processors',
    consumerId: 'worker-1',
    concurrency: 10
  },
  async (message) => {
    console.log('Processing event:', message);
    await handleUserEvent(message);
  }
);

// Multiple consumers can process the same stream
await streams.createReceiver(
  {
    groupId: 'analytics-processors',
    consumerId: 'analytics-worker'
  },
  async (message) => {
    await trackAnalytics(message);
  }
);
```

## 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/rpc-client
Description: Type-safe RPC client for making remote procedure calls. Provides automatic serialization, error handling, and request memoization.
Author: Tobias Herber
Latest version: 1.1.3

# `@lowerdeck/rpc-client`

Type-safe RPC client for making remote procedure calls. Provides automatic serialization, error handling, and request memoization.

## Installation

```bash
npm install @lowerdeck/rpc-client
yarn add @lowerdeck/rpc-client
bun add @lowerdeck/rpc-client
pnpm add @lowerdeck/rpc-client
```

## Usage

```typescript
import { createClient } from '@lowerdeck/rpc-client';

// Define your API interface
interface API {
  getUser(id: string): Promise<{ name: string; email: string }>;
  createPost(data: { title: string; content: string }): Promise<{ id: string }>;
}

// Create a type-safe client
const client = createClient<API>({
  url: 'https://api.example.com/rpc'
});

// Make RPC calls with full type safety
const user = await client.getUser('user_123');
console.log(user.name);

const post = await client.createPost({
  title: 'Hello World',
  content: 'This is my first post'
});
console.log(post.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/rpc-server
Description: Type-safe RPC server for handling remote procedure calls. Provides automatic validation, error handling, execution context tracking, and Sentry integration.
Author: Tobias Herber
Latest version: 1.0.10

# `@lowerdeck/rpc-server`

Type-safe RPC server for handling remote procedure calls. Provides automatic validation, error handling, execution context tracking, and Sentry integration.

## Installation

```bash
npm install @lowerdeck/rpc-server
yarn add @lowerdeck/rpc-server
bun add @lowerdeck/rpc-server
pnpm add @lowerdeck/rpc-server
```

## Usage

```typescript
import { createRpcServer } from '@lowerdeck/rpc-server';

// Define your API implementation
const api = {
  getUser: async (id: string) => {
    return { name: 'John Doe', email: 'john@example.com' };
  },

  createPost: async (data: { title: string; content: string }) => {
    return { id: 'post_123' };
  }
};

// Create the RPC server
const server = createRpcServer({
  controllers: { api },
  context: async (req) => ({
    userId: req.headers.get('user-id')
  })
});

// Use with your HTTP framework
app.post('/rpc', server.handler);
```

## 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/sentry
Description: Lightweight wrapper for managing Sentry instances across your application. Provides a global reference that can be configured and accessed throughout your codebase.
Author: Tobias Herber
Latest version: 1.0.2

# `@lowerdeck/sentry`

Lightweight wrapper for managing Sentry instances across your application. Provides a global reference that can be configured and accessed throughout your codebase.

## Installation

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

## Usage

```typescript
import { setSentry, getSentry } from '@lowerdeck/sentry';
import * as Sentry from '@sentry/node';

// Initialize Sentry in your application
Sentry.init({
  dsn: 'your-dsn-here',
  environment: 'production'
});

// Set the Sentry instance
setSentry(Sentry);

// Access Sentry from anywhere in your application
const sentry = getSentry();
sentry.captureException(new Error('Something went wrong'));

// In other modules
import { getSentry } from '@lowerdeck/sentry';

function logError(error: Error) {
  const sentry = getSentry();
  sentry.captureException(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/serialize
Description: Enhanced serialization using SuperJSON to handle complex types that standard JSON cannot encode. Preserves Maps, Sets, Dates, and other non-standard JSON types.
Author: Tobias Herber
Latest version: 1.0.4

# `@lowerdeck/serialize`

Enhanced serialization using SuperJSON to handle complex types that standard JSON cannot encode. Preserves Maps, Sets, Dates, and other non-standard JSON types.

## Installation

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

## Usage

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

// Encode complex data types
const data = {
  date: new Date('2024-01-01'),
  map: new Map([['key', 'value']]),
  set: new Set([1, 2, 3]),
  undefined: undefined,
  bigint: 123n
};

const encoded = serialize.encode(data);
console.log(encoded); // JSON string with type metadata

// Decode back to original types
const decoded = serialize.decode(encoded);
console.log(decoded.date instanceof Date); // true
console.log(decoded.map instanceof Map); // true
console.log(decoded.set instanceof Set); // true

// Perfect for storing in localStorage or databases
localStorage.setItem('data', serialize.encode(data));
const restored = serialize.decode(localStorage.getItem('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/service
Description: Wrap service methods with OpenTelemetry tracing. Automatically creates spans for each method call with error tracking and status reporting.
Author: Tobias Herber
Latest version: 1.0.7

# `@lowerdeck/service`

Wrap service methods with OpenTelemetry tracing. Automatically creates spans for each method call with error tracking and status reporting.

## Installation

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

## Usage

```typescript
import { Service } from '@lowerdeck/service';

// Define your service
class UserService {
  async getUser(id: string) {
    // Your business logic
    return await database.users.findById(id);
  }

  async createUser(data: CreateUserData) {
    return await database.users.create(data);
  }

  async updateUser(id: string, data: UpdateUserData) {
    return await database.users.update(id, data);
  }
}

// Wrap with tracing
const userService = Service.create('user-service', () => new UserService())
  .build();

// All methods now have automatic tracing
await userService.getUser('123');
// Creates span: user-service.getUser

await userService.createUser({ name: 'John' });
// Creates span: user-service.createUser

// Errors are automatically tracked
try {
  await userService.getUser('invalid-id');
} catch (error) {
  // Span is marked with error status
}
```

### Benefits

- Automatic span creation for each method
- Error tracking with span status
- Method-level performance monitoring
- Zero boilerplate tracing code

## 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/shadow-id
Description: Generate consistent, unique shadow IDs based on input data. Combines SHA256 hashing, base62 encoding, and timestamp extraction for deterministic ID generation.
Author: Tobias Herber
Latest version: 1.0.7

# `@lowerdeck/shadow-id`

Generate consistent, unique shadow IDs based on input data. Combines SHA256 hashing, base62 encoding, and timestamp extraction for deterministic ID generation.

## Installation

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

## Usage

```typescript
import { shadowId } from '@lowerdeck/shadow-id';

// Generate shadow ID from user data
const id = await shadowId(
  'user',                           // prefix
  ['session-123', 'device-456'],    // IDs to combine
  ['john@example.com', 1234567890]  // additional data
);
console.log(id); // 'user_xY9zK1mN2pQ3r4s5...'

// Same inputs always produce the same ID
const id1 = await shadowId('user', ['123'], ['john@example.com']);
const id2 = await shadowId('user', ['123'], ['john@example.com']);
console.log(id1 === id2); // true

// Different inputs produce different IDs
const id3 = await shadowId('user', ['456'], ['jane@example.com']);
console.log(id1 !== id3); // true

// Useful for creating deterministic anonymous IDs
const anonymousUserId = await shadowId(
  'anon',
  [sessionId, deviceId],
  [ipAddress, userAgent]
);
```

## 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/sign
Description: HMAC signing and verification with expiration support. Uses HMAC-SHA512 and base62 encoding for secure, compact signatures.
Author: Tobias Herber
Latest version: 1.0.5

# `@lowerdeck/sign`

HMAC signing and verification with expiration support. Uses HMAC-SHA512 and base62 encoding for secure, compact signatures.

## Installation

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

## Usage

### With Expiration

```typescript
import { signature } from '@lowerdeck/sign';

const signer = signature({
  prefix: 'token',
  expirationMs: 3600000, // 1 hour
  key: 'your-secret-key'
});

// Sign data with expiration
const signed = await signer.sign('user-123');
console.log(signed); // 'token_xyz123abc...'

// Verify signature
const verified = await signer.verify(signed);
if (verified) {
  console.log('Valid signature');
} else {
  console.log('Invalid or expired signature');
}
```

### Without Expiration

```typescript
import { signatureBasic } from '@lowerdeck/sign';

// Simple signing without expiration
const signed = await signatureBasic.sign('data', 'secret-key');
console.log(signed); // 'data.xyz123abc...'

const isValid = await signatureBasic.verify(signed, 'secret-key');
console.log(isValid); // 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/slugify
Description: Generate URL-friendly slugs with collision detection. Automatically handles uniqueness and provides short ID generation with random padding.
Author: Tobias Herber
Latest version: 1.0.5

# `@lowerdeck/slugify`

Generate URL-friendly slugs with collision detection. Automatically handles uniqueness and provides short ID generation with random padding.

## Installation

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

## Usage

### Basic Slugify

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

// Convert to URL-safe slug
slugify('Hello World!');  // 'hello-world'
slugify('This & That');   // 'this-that'
slugify('Café ☕');       // 'cafe'
```

### Slug Generator with Collision Detection

```typescript
import { createSlugGenerator } from '@lowerdeck/slugify';

// Create generator with collision checker
const generateSlug = createSlugGenerator(async (slug) => {
  // Check if slug exists in database
  return await db.posts.exists({ slug });
});

// Generate unique slug
const slug1 = await generateSlug('My Post');        // 'my-post'
const slug2 = await generateSlug('My Post');        // 'my-post-abc123'
const slug3 = await generateSlug('My Post');        // 'my-post-xyz789'
```

### Short ID Generator

```typescript
import { createShortIdGenerator } from '@lowerdeck/slugify';

// Create short ID generator
const generateShortId = createShortIdGenerator(
  async (id) => await db.links.exists({ id }),
  { minLength: 6 }
);

// Generate short IDs with collision avoidance
const id1 = await generateShortId('product'); // 'produc'
const id2 = await generateShortId('prod');    // 'prod12' (padded to min length)
```

## 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/telemetry
Description: Shared OpenTelemetry bootstrap and execution-context trace fallback helpers.
Author: Ivan Siianytsia
Latest version: 1.1.1

# `@lowerdeck/telemetry`

Shared OpenTelemetry bootstrap and execution-context trace fallback helpers.

## Installation

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

## Usage

```typescript
import { initTelemetry, withExecutionContextTraceFallback } from '@lowerdeck/telemetry';

initTelemetry({
  serviceName: 'my-service',
  allowRootSpans: true
});

let result = await withExecutionContextTraceFallback(async () => {
  return await doWork();
});
```

Notes:
- Root spans are disabled by default.
- Set `allowRootSpans: true` for services that should create root traces themselves.
- `OTEL_ALLOW_ROOT_SPANS=true` also enables root spans.

## Environment Variables

### `OTEL_ENABLED`
- Type: `'true' | 'false'`
- Default: `'false'`
- What it does: enables telemetry initialization and trace fallback behavior.

Example:

```bash
export OTEL_ENABLED=true
```

### `OTEL_EXPORTER_OTLP_TRACES_ENDPOINT`
- Type: string (URL)
- Required when `OTEL_ENABLED=true`
- What it does: full OTLP traces endpoint used by the HTTP exporter.

Example:

```bash
export OTEL_EXPORTER_OTLP_TRACES_ENDPOINT="https://example.com/otlp/v1/traces"
```

### `OTEL_EXPORTER_OTLP_HEADERS`
- Type: comma-separated `key=value` pairs
- Default: empty
- What it does: custom HTTP headers sent with OTLP export requests (typically auth).

Example:

```bash
export OTEL_EXPORTER_OTLP_HEADERS="x-sentry-auth=sentry sentry_key=secret-key"
```

### `OTEL_ALLOW_ROOT_SPANS`
- Type: `'true' | 'false'`
- Default: `'false'`
- What it does: enables creating root spans in the service when no parent trace exists.

Example:

```bash
export OTEL_ALLOW_ROOT_SPANS=true
```

### `OTEL_SERVICE_NAME`
- Type: string
- Default: value passed to `initTelemetry({ serviceName })`
- What it does: overrides the service name resource attribute.

Example:

```bash
export OTEL_SERVICE_NAME="subspace-controller"
```

### `OTEL_RESOURCE_ATTRIBUTES`
- Type: comma-separated `key=value` pairs
- Default: empty
- What it does: adds extra OTEL resource attributes to every exported span.

Example:

```bash
export OTEL_RESOURCE_ATTRIBUTES="service.namespace=subspace,deployment.environment=development,team=platform"
```

### `OTEL_DEBUG`
- Type: `'true' | 'false'`
- Default: `'false'`
- What it does: enables OpenTelemetry diagnostic logging.

Example:

```bash
export OTEL_DEBUG=true
```

Note:
- `deployment.environment.name` resource attribute defaults to `METORIAL_ENV`, then `NODE_ENV`, then `'development'`.
npm package: @lowerdeck/testing-tools
Description: Opinionated testing utilities for Vitest configuration, environment loading, Prisma test databases, factories, and HTTP helpers.
Author: Ivan Siianytsia
Latest version: 1.0.1

# `@lowerdeck/testing-tools`

Opinionated testing utilities for Vitest configuration, environment loading, Prisma test databases, factories, and HTTP helpers.

## Installation

```bash
npm install @lowerdeck/testing-tools
```

```bash
yarn add @lowerdeck/testing-tools
```

```bash
bun add @lowerdeck/testing-tools
```

```bash
pnpm add @lowerdeck/testing-tools
```

## Usage

### Vitest config

```typescript
import { createVitestConfig, loadTestEnv, withAliases } from '@lowerdeck/testing-tools';
// In CI, run Vitest with `--mode ci` to load `.env.ci` via loadTestEnv.

loadTestEnv();

const config = withAliases(createVitestConfig(), { '@': './src' });

export default config;
```

### Prisma test database helpers

Use `setupPrismaTestDb` in `setupFiles` (runs before the Vitest runner),
and `withTestDb` inside test files when you want hook-based cleanup.

```typescript
// vitest.setup.ts (setupFiles)
import { setupPrismaTestDb } from '@lowerdeck/testing-tools';
import { PrismaClient } from '@prisma/client';

const db = await setupPrismaTestDb({
  prismaClientFactory: url => new PrismaClient({ datasourceUrl: url }),
  guard: 'test'
});

export const testDb = db.client;
export const cleanDatabase = db.clean;
```

Call `db.disconnect()` in an `afterAll` hook when the test process shuts down.

```typescript
// inside a test file usage
import { withTestDb } from '@lowerdeck/testing-tools';
import { PrismaClient } from '@prisma/client';

const { client } = withTestDb({
  prismaClientFactory: url => new PrismaClient({ datasourceUrl: url }),
  guard: 'test',
  cleanBeforeEach: true
});
```

### Factories

```typescript
import { defineFactory } from '@lowerdeck/testing-tools';

type User = { name: string; email: string };

const userFactory = defineFactory<User>(
  () => ({ name: 'Ada', email: 'ada@example.com' }),
  {
    persist: async user => user
  }
);
```

### HTTP test helpers

```typescript
import { createTestServer } from '@lowerdeck/testing-tools';

const server = createTestServer({
  request: async path => new Response(`ok:${path}`)
});

await server.get('/health');
```

## 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/timezone
Description: Access a comprehensive timezone information database. Provides timezone names, codes, UTC offsets, and labels for all major timezones.
Author: Tobias Herber
Latest version: 1.0.4

# `@lowerdeck/timezone`

Access a comprehensive timezone information database. Provides timezone names, codes, UTC offsets, and labels for all major timezones.

## Installation

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

## Usage

```typescript
import { getTimezones, getTimezone, defaultTimezone } from '@lowerdeck/timezone';

// Get all timezones
const allTimezones = getTimezones();
console.log(allTimezones.length); // ~400+ timezones

// Get specific timezone by code or ID
const timezone = getTimezone('America/New_York');
console.log(timezone);
// {
//   label: '(GMT-05:00) Eastern Time',
//   tzCode: 'America/New_York',
//   name: 'Eastern Time',
//   utc: '-05:00',
//   id: 123,
//   utcOffsetInMinutes: -300
// }

// Get by numeric ID
const tz = getTimezone(123);

// Use default timezone
console.log(defaultTimezone);
// {
//   label: '(GMT+00:00) UTC',
//   tzCode: 'UTC',
//   ...
// }

// Build timezone selector
const timezoneOptions = getTimezones().map(tz => ({
  value: tz.tzCode,
  label: tz.label
}));
```

## 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/tokens
Description: Create and verify signed tokens with HMAC or asymmetric cryptography. Supports expiration checking and type validation with a clean token format.
Author: Tobias Herber
Latest version: 1.0.5

# `@lowerdeck/tokens`

Create and verify signed tokens with HMAC or asymmetric cryptography. Supports expiration checking and type validation with a clean token format.

## Installation

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

## Usage

```typescript
import { Tokens } from '@lowerdeck/tokens';

// Create token manager with HMAC-SHA384
const tokens = new Tokens({
  alg: 'HS384',
  secret: 'your-secret-key'
});

// Sign a token
const token = await tokens.sign({
  type: 'reset-password',
  data: { userId: '123', email: 'user@example.com' },
  expiresAt: new Date(Date.now() + 3600000) // 1 hour
});
console.log(token); // 'reset-password_v1_eyJ1c2VySWQi..._xyz123'

// Verify and decode
const result = await tokens.verify({
  token,
  expectedType: 'reset-password'
});

if (result.success) {
  console.log(result.data); // { userId: '123', email: '...' }
} else {
  console.log(result.error); // 'expired', 'invalid-signature', 'wrong-type'
}

// Asymmetric signing (RS256, ES384)
const rsaTokens = new Tokens({
  alg: 'RS256',
  privateKey: privateKeyPEM,
  publicKey: publicKeyPEM
});
```

## Token Format

Tokens follow the format: `type_v1_data_signature`

## 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/unique
Description: Remove duplicate values from arrays using Set-based deduplication. Preserves the first occurrence of each unique value.
Author: Tobias Herber
Latest version: 1.0.4

# `@lowerdeck/unique`

Remove duplicate values from arrays using Set-based deduplication. Preserves the first occurrence of each unique value.

## Installation

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

## Usage

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

// Remove duplicate numbers
const numbers = unique([1, 2, 2, 3, 3, 3, 4]);
console.log(numbers); // [1, 2, 3, 4]

// Remove duplicate strings
const tags = unique(['javascript', 'typescript', 'javascript', 'react']);
console.log(tags); // ['javascript', 'typescript', 'react']

// Works with any value that can be compared with Set
const mixed = unique([1, '1', 2, '2', 1, 2]);
console.log(mixed); // [1, '1', 2, '2']
```

## 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/websocket-client
Description: Auto-reconnecting WebSocket client with exponential backoff. Handles connection failures and provides a familiar event-driven API.
Author: Tobias Herber
Latest version: 1.0.4

# `@lowerdeck/websocket-client`

Auto-reconnecting WebSocket client with exponential backoff. Handles connection failures and provides a familiar event-driven API.

## Installation

```bash
npm install @lowerdeck/websocket-client
yarn add @lowerdeck/websocket-client
bun add @lowerdeck/websocket-client
pnpm add @lowerdeck/websocket-client
```

## Usage

```typescript
import { ReconnectingWebSocketClient } from '@lowerdeck/websocket-client';

// Create client with automatic reconnection
const ws = new ReconnectingWebSocketClient('wss://example.com/socket', {
  protocols: ['v1'],
  maxAttempts: 5,
  timeout: 5000,
  onReconnect: (attempt) => {
    console.log(`Reconnecting... Attempt ${attempt}`);
  }
});

// Listen for events
ws.addEventListener('open', () => {
  console.log('Connected');
});

ws.addEventListener('message', (event) => {
  console.log('Received:', event.data);
});

ws.addEventListener('close', () => {
  console.log('Disconnected');
});

ws.addEventListener('error', (error) => {
  console.error('Error:', error);
});

ws.addEventListener('maximum', () => {
  console.error('Max reconnection attempts reached');
});

// Send messages
ws.send('Hello server');
ws.send(JSON.stringify({ type: 'ping' }));

// Close connection
ws.close();

// Check connection state
console.log(ws.readyState); // 0: CONNECTING, 1: OPEN, 2: CLOSING, 3: CLOSED
```

### Reconnection Behavior

- Automatically reconnects on connection loss
- Exponential backoff between attempts
- Maximum retry attempts configurable
- Manual close prevents reconnection

## 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>