open-index/open-npm huggingface dataset:
npm package: @philiprehberger/tiny-store
Description: Reactive state container in under 1KB — framework agnostic
Author: Philip Rehberger
Latest version: 0.1.4

# @philiprehberger/tiny-store

[![CI](https://github.com/philiprehberger/ts-tiny-store/actions/workflows/ci.yml/badge.svg)](https://github.com/philiprehberger/ts-tiny-store/actions/workflows/ci.yml)
[![npm version](https://img.shields.io/npm/v/@philiprehberger/tiny-store.svg)](https://www.npmjs.com/package/@philiprehberger/tiny-store)
[![Last updated](https://img.shields.io/github/last-commit/philiprehberger/ts-tiny-store)](https://github.com/philiprehberger/ts-tiny-store/commits/main)

Reactive state container in under 1KB — framework agnostic

## Installation

```bash
npm install @philiprehberger/tiny-store
```

## Usage

```ts
import { createStore, computed, batch } from '@philiprehberger/tiny-store';

// Create a store
const count = createStore(0);
count.get(); // 0

// Subscribe to changes
const unsubscribe = count.subscribe((value, previous) => {
  console.log(`${previous} -> ${value}`);
});

count.set(1); // logs: 0 -> 1
count.update((n) => n + 1); // logs: 1 -> 2

// Derived stores
const doubled = computed([count], (n) => n * 2);
doubled.get(); // 4

// Batch updates — subscribers notified once per set, after batch completes
batch(() => {
  count.set(10);
  count.set(20);
});

unsubscribe();
```

## API

### `createStore<T>(initial: T): Store<T>`

Create a reactive store with an initial value.

| Method | Description |
| --- | --- |
| `get()` | Return the current value |
| `set(value)` | Replace the value and notify subscribers (skipped if `Object.is` equal) |
| `update(fn)` | Update via callback: `store.update(n => n + 1)` |
| `subscribe(listener)` | Register a `(value, previous) => void` listener; returns an unsubscribe function |

### `computed<T>(stores, fn): ComputedStore<T>`

Create a read-only derived store that recomputes whenever any source store changes. Subscribers are only notified when the computed result actually differs (`Object.is`).

### `batch(fn: () => void): void`

Execute `fn` synchronously, deferring all subscriber notifications until the batch completes. Supports nesting. If `fn` throws, pending notifications are still flushed.

## Development

```bash
npm install
npm run build
npm run typecheck
npm test
```

## Support

If you find this project useful:

⭐ [Star the repo](https://github.com/philiprehberger/ts-tiny-store)

🐛 [Report issues](https://github.com/philiprehberger/ts-tiny-store/issues?q=is%3Aissue+is%3Aopen+label%3Abug)

💡 [Suggest features](https://github.com/philiprehberger/ts-tiny-store/issues?q=is%3Aissue+is%3Aopen+label%3Aenhancement)

❤️ [Sponsor development](https://github.com/sponsors/philiprehberger)

🌐 [All Open Source Projects](https://philiprehberger.com/open-source-packages)

💻 [GitHub Profile](https://github.com/philiprehberger)

🔗 [LinkedIn Profile](https://www.linkedin.com/in/philiprehberger)

## License

[MIT](LICENSE)
npm package: @philiprehberger/ts-api-error-kit
Description: Standardized typed HTTP error classes for APIs
Author: Philip Rehberger
Latest version: 0.1.5

# @philiprehberger/ts-api-error-kit

[![CI](https://github.com/philiprehberger/ts-api-error-kit/actions/workflows/ci.yml/badge.svg)](https://github.com/philiprehberger/ts-api-error-kit/actions/workflows/ci.yml)
[![npm version](https://img.shields.io/npm/v/@philiprehberger/ts-api-error-kit.svg)](https://www.npmjs.com/package/@philiprehberger/ts-api-error-kit)
[![License](https://img.shields.io/github/license/philiprehberger/ts-api-error-kit)](LICENSE)

Standardized typed HTTP error classes for APIs

## Installation

```bash
npm install @philiprehberger/ts-api-error-kit
```

## Usage

### Throwing Errors

```ts
import { ApiError, NotFoundError, BadRequestError } from '@philiprehberger/ts-api-error-kit';

// Generic error with status code
throw new ApiError(404, 'User not found', { code: 'USER_NOT_FOUND' });

// Convenience subclasses
throw new NotFoundError('User not found');
throw new BadRequestError('Invalid email', { code: 'INVALID_EMAIL', details: { field: 'email' } });
```

### Serializing Errors

```ts
import { toErrorResponse } from '@philiprehberger/ts-api-error-kit';

app.use((err, req, res, next) => {
  const response = toErrorResponse(err);
  res.status(response.status).json(response);
});

// ApiError → { status: 404, message: 'User not found', code: 'USER_NOT_FOUND' }
// Unknown  → { status: 500, message: 'Internal server error' }
```

### Type Guard

```ts
import { isApiError } from '@philiprehberger/ts-api-error-kit';

if (isApiError(err)) {
  console.log(err.statusCode); // typed
}
```

## Error Classes

| Class | Status Code |
|-------|------------|
| `ApiError` | Any (first argument) |
| `BadRequestError` | 400 |
| `UnauthorizedError` | 401 |
| `ForbiddenError` | 403 |
| `NotFoundError` | 404 |
| `ConflictError` | 409 |
| `UnprocessableError` | 422 |
| `InternalError` | 500 |

## API

| Export | Description |
|--------|-------------|
| `ApiError` | Base error class with `statusCode`, `code`, `details` |
| `isApiError(err)` | Type guard — returns `true` if `err` is an `ApiError` |
| `toErrorResponse(err)` | Serializes any error to `ErrorResponse` object |


## Development

```bash
npm install
npm run build
npm test
```

## License

MIT
npm package: @philiprehberger/ts-async-batcher
Description: Automatic batching and deduplication for async operations
Author: Philip Rehberger
Latest version: 0.1.4

# @philiprehberger/ts-async-batcher

[![CI](https://github.com/philiprehberger/ts-async-batcher/actions/workflows/ci.yml/badge.svg)](https://github.com/philiprehberger/ts-async-batcher/actions/workflows/ci.yml)
[![npm version](https://img.shields.io/npm/v/@philiprehberger/ts-async-batcher.svg)](https://www.npmjs.com/package/@philiprehberger/ts-async-batcher)
[![License](https://img.shields.io/github/license/philiprehberger/ts-async-batcher)](LICENSE)

Automatic batching and deduplication for async operations

## Installation

```bash
npm install @philiprehberger/ts-async-batcher
```

## Usage

```ts
import { createBatcher } from '@philiprehberger/ts-async-batcher';

const userLoader = createBatcher(async (ids: string[]) => {
  // Called once with all collected IDs
  return db.users.findMany({ where: { id: { in: ids } } });
});

// These are automatically batched into a single call
const user1 = await userLoader.load('user-1');
const user2 = await userLoader.load('user-2');

// Load multiple at once
const users = await userLoader.loadMany(['user-3', 'user-4']);
```

### Options

```ts
const loader = createBatcher(batchFn, {
  maxBatchSize: 100,  // Flush when batch reaches this size (default: 100)
  windowMs: 10,       // Batch window in milliseconds (default: 10)
});
```

### How It Works

1. `load(key)` adds the key to an internal queue and returns a promise
2. After `windowMs` or when `maxBatchSize` is reached, the queue is flushed
3. Duplicate keys within a batch window are deduplicated — the batch function receives unique keys only
4. Results are matched back to callers by index (batch function must return results in the same order as keys)

## API

| Export | Description |
|--------|-------------|
| `createBatcher(batchFn, options?)` | Create a new batcher instance |

### `Batcher<K, V>`

| Method | Description |
|--------|-------------|
| `load(key)` | Load a single value, batched automatically |
| `loadMany(keys)` | Load multiple values, returns `Promise<V[]>` |

### `BatcherOptions`

| Option | Type | Default | Description |
|--------|------|---------|-------------|
| `maxBatchSize` | `number` | `100` | Max keys per batch |
| `windowMs` | `number` | `10` | Batch collection window in ms |


## Development

```bash
npm install
npm run build
npm test
```

## License

MIT
npm package: @philiprehberger/ts-cloudinary-utils
Description: Cloudinary image URL builder, srcset generation, and blur placeholders
Author: Philip Rehberger
Latest version: 0.2.1

# @philiprehberger/ts-cloudinary-utils

Cloudinary image URL builder, srcset generation, and blur placeholders.

## Installation

```bash
npm install @philiprehberger/ts-cloudinary-utils
```

## Usage

```ts
import { buildCloudinaryUrl, buildCloudinarySrcset, createCloudinaryLoader } from '@philiprehberger/ts-cloudinary-utils';

const url = buildCloudinaryUrl('my-cloud', 'folder/image.jpg', {
  width: 800,
  quality: 'auto',
  format: 'webp',
  crop: 'fill',
});

const srcset = buildCloudinarySrcset('my-cloud', 'folder/image.jpg');

// Next.js Image loader
const loader = createCloudinaryLoader('my-cloud');
// <Image loader={loader} src="folder/image.jpg" ... />
```

### Blur Placeholders

```ts
import { shimmerPlaceholder, generateBlurPlaceholder, gradientPlaceholder } from '@philiprehberger/ts-cloudinary-utils';

const shimmer = shimmerPlaceholder(300, 300);
const blur = generateBlurPlaceholder(300, 200, '#f0f0f0');
const gradient = gradientPlaceholder('#f0f0f0', '#e0e0e0', 'diagonal');
```

## API Reference

### URL Building

| Function | Signature | Description |
|----------|-----------|-------------|
| `buildCloudinaryUrl` | `(cloudName, publicId, options?) => string` | Build a Cloudinary URL with transforms. |
| `buildCloudinaryBlurPlaceholder` | `(cloudName, publicId) => string` | Low-quality blurred placeholder URL. |
| `buildCloudinarySrcset` | `(cloudName, publicId, widths?, format?) => string` | Responsive srcset string. Default widths: 320–1536. |

### `CloudinaryTransformOptions`

| Property | Type | Description |
|----------|------|-------------|
| `width` | `number` | Output width in pixels. |
| `height` | `number` | Output height in pixels. |
| `quality` | `'auto' \| number` | Image quality. Default: `'auto'`. |
| `format` | `'auto' \| 'webp' \| 'avif' \| 'jpg' \| 'png'` | Output format. Default: `'auto'`. |
| `crop` | `'fill' \| 'fit' \| 'scale' \| 'thumb'` | Crop mode. |
| `gravity` | `'auto' \| 'center' \| 'face' \| 'faces'` | Crop gravity. |
| `blur` | `number` | Blur effect strength. |

### Next.js Integration

| Function | Signature | Description |
|----------|-----------|-------------|
| `createCloudinaryLoader` | `(cloudName) => ImageLoader` | Returns a loader compatible with Next.js `<Image>`. |

### Blur Placeholders

| Function | Description |
|----------|-------------|
| `generateBlurPlaceholder(w, h, color?)` | Solid color SVG as base64 data URI. |
| `shimmerPlaceholder(w, h)` | Animated shimmer (light theme). |
| `shimmerPlaceholderDark(w, h)` | Animated shimmer (dark theme). |
| `colorPlaceholder(hexColor)` | 1x1 color swatch. |
| `gradientPlaceholder(from, to, direction?)` | Gradient placeholder. Directions: `'horizontal'` `'vertical'` `'diagonal'`. |
| `placeholders` | Static object with `gray`, `white`, `dark`, `transparent` 1px PNGs. |

## License

MIT
npm package: @philiprehberger/ts-config-layer
Description: Layered configuration loader with typed output and multiple sources
Author: Philip Rehberger
Latest version: 0.1.4

# @philiprehberger/ts-config-layer

[![CI](https://github.com/philiprehberger/ts-config-layer/actions/workflows/publish.yml/badge.svg)](https://github.com/philiprehberger/ts-config-layer/actions/workflows/publish.yml)
[![npm version](https://img.shields.io/npm/v/@philiprehberger/ts-config-layer.svg)](https://www.npmjs.com/package/@philiprehberger/ts-config-layer)
[![License](https://img.shields.io/github/license/philiprehberger/ts-config-layer)](LICENSE)

Layered configuration loader with typed output and multiple sources.

## Installation

```bash
npm install @philiprehberger/ts-config-layer
```

## Usage

```ts
import { loadConfig, envSource, objectSource } from '@philiprehberger/ts-config-layer';

const config = loadConfig({
  port: { type: 'number', default: 3000 },
  dbUrl: { type: 'string', required: true, env: 'DATABASE_URL' },
  debug: { type: 'boolean', default: false },
}, [
  envSource(),
  objectSource({ port: 8080 }),
]);

// config.port → 8080 (from objectSource, overrides default)
// config.dbUrl → from DATABASE_URL env var
// config.debug → false (default)
```

### Sources

Sources are applied in order — later sources override earlier ones:

```ts
const config = loadConfig(schema, [
  envSource(),                    // Load from process.env
  envSource('APP_'),              // Load from env vars with APP_ prefix
  objectSource({ port: 9090 }),   // Override with explicit values
]);
```

### Schema

```ts
const schema = {
  // Shorthand
  host: 'string',
  port: 'number',
  debug: 'boolean',

  // Full definition
  dbUrl: {
    type: 'string',
    required: true,     // Throws if missing (default: true)
    env: 'DATABASE_URL', // Map to specific env var name
    default: undefined,  // Default value if not provided
  },
};
```

## API

| Export | Description |
|--------|-------------|
| `loadConfig(schema, sources)` | Load and validate config from sources |
| `envSource(prefix?)` | Read from `process.env`, optionally filtering by prefix |
| `objectSource(obj)` | Read from a plain object |

### `ConfigFieldDef`

| Property | Type | Default | Description |
|----------|------|---------|-------------|
| `type` | `'string' \| 'number' \| 'boolean'` | — | Value type (auto-coerced) |
| `required` | `boolean` | `true` | Throw if missing |
| `default` | `string \| number \| boolean` | — | Default value |
| `env` | `string` | — | Override env var name |


## Development

```bash
npm install
npm run build
npm test
```

## License

MIT
npm package: @philiprehberger/ts-enum
Description: Rich enums with methods labels and serialization
Author: Philip Rehberger
Latest version: 0.1.3

# @philiprehberger/ts-enum

[![CI](https://github.com/philiprehberger/ts-enum/actions/workflows/publish.yml/badge.svg)](https://github.com/philiprehberger/ts-enum/actions/workflows/publish.yml)
[![npm version](https://img.shields.io/npm/v/@philiprehberger/ts-enum.svg)](https://www.npmjs.com/package/@philiprehberger/ts-enum)
[![License](https://img.shields.io/github/license/philiprehberger/ts-enum)](LICENSE)

Rich enums with methods, labels, and serialization.

## Installation

```bash
npm install @philiprehberger/ts-enum
```

## Usage

```ts
import { defineEnum, type EnumValue } from '@philiprehberger/ts-enum';

const Status = defineEnum({
  ACTIVE:   { value: 'active',   label: 'Active' },
  INACTIVE: { value: 'inactive', label: 'Inactive' },
});

type Status = EnumValue<typeof Status>; // 'active' | 'inactive'

Status.ACTIVE;          // 'active' (literal type)
Status.label('active'); // 'Active'
Status.options();       // [{ value: 'active', label: 'Active' }, ...]
Status.parse('active'); // { ok: true, value: 'active' }
Status.is('active');    // true (type guard)
```

### Simple Enums

```ts
const Color = defineEnum({
  RED: 'red',
  GREEN: 'green',
  BLUE: 'blue',
});

Color.RED;       // 'red'
Color.values();  // ['red', 'green', 'blue']
```

## API

| Method | Description |
|--------|-------------|
| `defineEnum(definition)` | Create a frozen enum with methods |
| `.values()` | All enum values |
| `.keys()` | All enum keys |
| `.entries()` | Key-value pairs |
| `.parse(input)` | Safe parse with result type |
| `.label(value)` | Get display label |
| `.is(value)` | Type guard |
| `.options()` | `{ value, label }[]` for UI selects |
| `.meta(value)` | Get metadata for a value |


## Development

```bash
npm install
npm run build
npm test
```

## License

MIT
npm package: @philiprehberger/ts-env-validator
Description: Schema-based environment variable validation with type-safe accessors
Author: Philip Rehberger
Latest version: 0.2.1

# @philiprehberger/ts-env-validator

Schema-based environment variable validation with type-safe accessors.

## Installation

```bash
npm install @philiprehberger/ts-env-validator
```

## Usage

```ts
import { createEnv } from '@philiprehberger/ts-env-validator';

const env = createEnv({
  DATABASE_URL: { type: 'url', required: true },
  PORT: { type: 'port', default: 3000 },
  NODE_ENV: { type: 'enum', values: ['development', 'production', 'test'] },
  ENABLE_CACHE: { type: 'boolean', default: false },
  ADMIN_EMAIL: { type: 'email', required: true },
  MAX_CONNECTIONS: { type: 'number', default: 10 },
  FEATURE_FLAGS: { type: 'json', default: {} },
});

env.DATABASE_URL; // string (typed, validated)
env.PORT;         // number
env.ENABLE_CACHE; // boolean
```

### Supported Types

| Type | Parses to | Validation |
|------|-----------|------------|
| `string` | `string` | Any non-empty string |
| `number` | `number` | Valid number |
| `boolean` | `boolean` | true/false/1/0/yes/no |
| `url` | `string` | Valid URL |
| `email` | `string` | Valid email format |
| `enum` | `string` | One of specified `values` |
| `port` | `number` | Integer 0–65535 |
| `json` | `unknown` | Valid JSON |

### Error Reporting

All validation errors are collected and reported at once:

```ts
// If DATABASE_URL and ADMIN_EMAIL are both missing:
// EnvValidationError: Environment validation failed:
//   Missing required environment variable: DATABASE_URL
//   Missing required environment variable: ADMIN_EMAIL
```

### Custom Source

```ts
const env = createEnv(schema, {
  DATABASE_URL: 'postgres://localhost/mydb',
  PORT: '8080',
});
```

## API Reference

### `createEnv<S extends Schema>(schema: S, source?: Record<string, string | undefined>): InferEnv<S>`

Validates environment variables against the given schema and returns a type-safe object. Throws `EnvValidationError` if any validation fails. If `source` is omitted, reads from `process.env`.

### `Schema`

A record mapping variable names to `FieldConfig` objects:

```ts
type Schema = Record<string, FieldConfig>;
```

### `FieldConfig`

| Property | Type | Description |
|----------|------|-------------|
| `type` | `'string' \| 'number' \| 'boolean' \| 'url' \| 'email' \| 'enum' \| 'port' \| 'json'` | Validation type. |
| `required` | `boolean` | Whether the variable must be present. Default: `false`. |
| `default` | Varies by type | Default value if missing. Makes the field effectively required in the output type. |
| `values` | `readonly string[]` | (enum only) Allowed values. |

### `EnvValidationError`

Thrown when one or more variables fail validation.

| Property | Type | Description |
|----------|------|-------------|
| `message` | `string` | Human-readable summary of all failures. |
| `errors` | `string[]` | Array of individual error messages. |

### `InferEnv<S>`

TypeScript utility type that infers the output type from a schema. Required fields and fields with defaults are non-optional; others are optional.

## License

MIT
npm package: @philiprehberger/ts-event-emitter
Description: Tiny, fully type-safe event emitter for Node, browser, and edge runtimes
Author: Philip Rehberger
Latest version: 0.2.1

# @philiprehberger/ts-event-emitter

Tiny, fully type-safe event emitter for Node, browser, and edge runtimes.

## Installation

```bash
npm install @philiprehberger/ts-event-emitter
```

## Usage

### Basic

```ts
import { createEmitter } from '@philiprehberger/ts-event-emitter';

type Events = {
  'user:login': { userId: string };
  'user:logout': { userId: string; reason?: string };
  'error': Error;
};

const emitter = createEmitter<Events>();

// Fully typed — event names and payloads are autocompleted
emitter.on('user:login', ({ userId }) => {
  console.log(`User ${userId} logged in`);
});
```

### Once / Off

```ts
// Listen once
emitter.once('user:login', ({ userId }) => { /* fires once */ });

// Manual unsubscribe
const off = emitter.on('error', (err) => console.error(err));
off(); // removes listener

// Or use off() directly
const handler = (data) => { /* ... */ };
emitter.on('user:logout', handler);
emitter.off('user:logout', handler);
```

### Wait for Event

```ts
const data = await emitter.waitFor('user:logout');
console.log(data.userId); // typed
```

### Wildcard Listener

```ts
emitter.onAny((event, data) => {
  console.log(`Event: ${String(event)}`, data);
});
```

### Cleanup

```ts
emitter.offAll('user:login'); // remove all listeners for one event
emitter.offAll();             // remove all listeners
```

### Listener Count

```ts
emitter.listenerCount('error'); // number
```

### Options

```ts
const emitter = createEmitter<Events>({
  maxListeners: 20, // warn if exceeded (default: 10)
});
```

## API Reference

### `createEmitter<E extends EventMap>(options?: EmitterOptions): Emitter<E>`

Creates a new type-safe emitter. `E` is a record mapping event names to their payload types.

### `EmitterOptions`

| Property | Type | Default | Description |
|----------|------|---------|-------------|
| `maxListeners` | `number` | `10` | Warn in console if exceeded. Set to `0` to disable. |

### `Emitter<E>` Methods

| Method | Signature | Description |
|--------|-----------|-------------|
| `on` | `(event, listener) => () => void` | Subscribe. Returns unsubscribe function. |
| `once` | `(event, listener) => () => void` | Subscribe for one emission only. |
| `off` | `(event, listener) => void` | Remove a specific listener. |
| `emit` | `(event, data) => void` | Emit an event to all listeners. |
| `waitFor` | `(event) => Promise<data>` | Returns a promise that resolves on next emission. |
| `onAny` | `(listener) => () => void` | Listen to all events. Listener receives `(event, data)`. |
| `offAll` | `(event?) => void` | Remove all listeners for an event, or all listeners if no event given. |
| `listenerCount` | `(event) => number` | Number of listeners for an event. |

## License

MIT
npm package: @philiprehberger/ts-feature-flag
Description: Simple in-memory feature flag system with percentage rollouts and targeting
Author: Philip Rehberger
Latest version: 0.1.4

# @philiprehberger/ts-feature-flag

[![CI](https://github.com/philiprehberger/ts-feature-flag/actions/workflows/publish.yml/badge.svg)](https://github.com/philiprehberger/ts-feature-flag/actions/workflows/publish.yml)
[![npm version](https://img.shields.io/npm/v/@philiprehberger/ts-feature-flag.svg)](https://www.npmjs.com/package/@philiprehberger/ts-feature-flag)
[![License](https://img.shields.io/github/license/philiprehberger/ts-feature-flag)](LICENSE)

Simple in-memory feature flag system with percentage rollouts and targeting.

## Installation

```bash
npm install @philiprehberger/ts-feature-flag
```

## Usage

```ts
import { createFlags } from '@philiprehberger/ts-feature-flag';

const flags = createFlags({
  newCheckout: true,
  darkMode: { percentage: 50 },
  betaFeature: { enabled: (ctx) => ctx.role === 'beta' },
});

flags.enabled('newCheckout');                     // true
flags.enabled('darkMode', { userId: 'abc' });     // deterministic by userId
flags.enabled('betaFeature', { role: 'beta' });   // true
flags.enabled('betaFeature', { role: 'user' });   // false
```

## API

| Export | Description |
|--------|-------------|
| `createFlags(definitions)` | Create a flags instance |

### Flag Types

| Type | Example | Description |
|------|---------|-------------|
| Boolean | `true` / `false` | Static on/off |
| Percentage | `{ percentage: 50 }` | Deterministic rollout by userId hash |
| Function | `{ enabled: (ctx) => ... }` | Custom targeting logic |


## Development

```bash
npm install
npm run build
npm test
```

## License

MIT
npm package: @philiprehberger/ts-guard
Description: Runtime type guard utilities with TypeScript narrowing
Author: Philip Rehberger
Latest version: 0.1.3

# @philiprehberger/ts-guard

[![CI](https://github.com/philiprehberger/ts-guard/actions/workflows/publish.yml/badge.svg)](https://github.com/philiprehberger/ts-guard/actions/workflows/publish.yml)
[![npm version](https://img.shields.io/npm/v/@philiprehberger/ts-guard.svg)](https://www.npmjs.com/package/@philiprehberger/ts-guard)
[![License](https://img.shields.io/github/license/philiprehberger/ts-guard)](LICENSE)

Runtime type guard utilities with TypeScript narrowing.

## Installation

```bash
npm install @philiprehberger/ts-guard
```

## Usage

```ts
import { isString, isNumber, isArrayOf, isObjectWith, assert } from '@philiprehberger/ts-guard';

if (isString(val)) {
  val.toUpperCase(); // narrowed to string
}

const isUser = isObjectWith({
  name: isString,
  age: isNumber,
});

if (isUser(data)) {
  data.name; // typed as string
  data.age;  // typed as number
}

assert(input, isArrayOf(isString)); // throws or narrows to string[]
```

### Composing Guards

```ts
import { isOneOf, isString, isNumber, isTupleOf } from '@philiprehberger/ts-guard';

const isStringOrNumber = isOneOf(isString, isNumber);
const isPair = isTupleOf(isString, isNumber);

isStringOrNumber('hello'); // true
isPair(['hello', 42]);     // true
```

## API

| Function | Description |
|----------|-------------|
| `isString`, `isNumber`, `isBoolean`, `isNull`, `isUndefined`, `isNil`, `isBigInt`, `isSymbol` | Primitive guards |
| `isArray`, `isObject`, `isPlainObject`, `isMap`, `isSet`, `isDate`, `isRegExp`, `isError`, `isPromise`, `isFunction` | Structural guards |
| `isNonEmpty`, `isPositive`, `isNegative`, `isInteger`, `isFiniteNumber`, `isNonNullish` | Value guards |
| `isOneOf(...guards)` | Union of guards |
| `isArrayOf(guard)` | Array where all items match guard |
| `isObjectWith(shape)` | Object matching shape of guards |
| `isTupleOf(...guards)` | Tuple matching guards positionally |
| `assert(value, guard, message?)` | Throw if guard fails, narrow otherwise |


## Development

```bash
npm install
npm run build
npm test
```

## License

MIT
npm package: @philiprehberger/ts-healthcheck
Description: Production readiness health check builder with typed check results
Author: Philip Rehberger
Latest version: 0.1.4

# @philiprehberger/ts-healthcheck

[![CI](https://github.com/philiprehberger/ts-healthcheck/actions/workflows/publish.yml/badge.svg)](https://github.com/philiprehberger/ts-healthcheck/actions/workflows/publish.yml)
[![npm version](https://img.shields.io/npm/v/@philiprehberger/ts-healthcheck.svg)](https://www.npmjs.com/package/@philiprehberger/ts-healthcheck)
[![License](https://img.shields.io/github/license/philiprehberger/ts-healthcheck)](LICENSE)

Production readiness health check builder with typed check results.

## Installation

```bash
npm install @philiprehberger/ts-healthcheck
```

## Usage

```ts
import { createHealthcheck, check } from '@philiprehberger/ts-healthcheck';

const health = createHealthcheck([
  check('database', async () => { await db.ping(); }),
  check('redis', async () => { await redis.ping(); }),
  check('api', async () => {
    const res = await fetch('https://api.example.com/health');
    if (!res.ok) throw new Error(`Status ${res.status}`);
  }),
]);

// Express/Fastify handler
app.get('/health', async (req, res) => {
  const result = await health();
  res.status(result.status === 'healthy' ? 200 : 503).json(result);
});
```

### Response

```json
{
  "status": "healthy",
  "checks": {
    "database": { "status": "healthy", "duration": 12 },
    "redis": { "status": "healthy", "duration": 3 },
    "api": { "status": "healthy", "duration": 45 }
  },
  "timestamp": "2026-03-13T10:00:00.000Z"
}
```

## API

| Export | Description |
|--------|-------------|
| `createHealthcheck(checks)` | Returns async function that runs all checks in parallel |
| `check(name, fn)` | Define a named health check |

### `HealthResult`

| Property | Type | Description |
|----------|------|-------------|
| `status` | `'healthy' \| 'degraded' \| 'unhealthy'` | Overall status |
| `checks` | `Record<string, CheckResult>` | Per-check results |
| `timestamp` | `string` | ISO timestamp |


## Development

```bash
npm install
npm run build
npm test
```

## License

MIT
npm package: @philiprehberger/ts-id-gen
Description: Flexible ID generation with UUID, ULID, nanoid, and prefixed IDs
Author: Philip Rehberger
Latest version: 0.1.4

# @philiprehberger/ts-id-gen

[![CI](https://github.com/philiprehberger/ts-id-gen/actions/workflows/publish.yml/badge.svg)](https://github.com/philiprehberger/ts-id-gen/actions/workflows/publish.yml)
[![npm version](https://img.shields.io/npm/v/@philiprehberger/ts-id-gen.svg)](https://www.npmjs.com/package/@philiprehberger/ts-id-gen)
[![License](https://img.shields.io/github/license/philiprehberger/ts-id-gen)](LICENSE)

Flexible ID generation with UUID, ULID, nanoid, and prefixed IDs. Zero dependencies.

## Installation

```bash
npm install @philiprehberger/ts-id-gen
```

## Usage

```ts
import { uuid, ulid, nanoid, prefixedId } from '@philiprehberger/ts-id-gen';

uuid();            // '550e8400-e29b-41d4-a716-446655440000'
ulid();            // '01ARZ3NDEKTSV4RRFFQ69G5FAV'
nanoid();          // 'V1StGXR8_Z5jdHi6B-myT' (21 chars)
nanoid(10);        // 'IRFa-VaY2b'
prefixedId('usr'); // 'usr_V1StGXR8_Z5jdHi6'
```

## API

| Function | Description |
|----------|-------------|
| `uuid()` | UUID v4 (RFC 4122) |
| `ulid()` | ULID (time-sortable) |
| `nanoid(size?)` | URL-safe nanoid (default 21 chars) |
| `prefixedId(prefix, size?)` | `{prefix}_{nanoid}` (default 16 char suffix) |


## Development

```bash
npm install
npm run build
npm test
```

## License

MIT
npm package: @philiprehberger/ts-pipe
Description: Typed functional pipe and compose for TypeScript
Author: Philip Rehberger
Latest version: 0.1.3

# @philiprehberger/ts-pipe

[![CI](https://github.com/philiprehberger/ts-pipe/actions/workflows/publish.yml/badge.svg)](https://github.com/philiprehberger/ts-pipe/actions/workflows/publish.yml)
[![npm version](https://img.shields.io/npm/v/@philiprehberger/ts-pipe.svg)](https://www.npmjs.com/package/@philiprehberger/ts-pipe)
[![License](https://img.shields.io/github/license/philiprehberger/ts-pipe)](LICENSE)

Typed functional pipe and compose for TypeScript.

## Installation

```bash
npm install @philiprehberger/ts-pipe
```

## Usage

```ts
import { pipe, pipeline, pipeAsync } from '@philiprehberger/ts-pipe';

const result = pipe(" Hello ", s => s.trim(), s => s.split(" "));
// string[] — fully inferred

const transform = pipeline(s => s.trim(), s => s.toLowerCase());
transform(" HELLO "); // "hello"

const data = await pipeAsync(userId, fetchUser, extractProfile, validate);
```

### Compose (right-to-left)

```ts
import { compose } from '@philiprehberger/ts-pipe';

const format = compose(
  (s: string) => `[${s}]`,
  (s: string) => s.toUpperCase(),
  (s: string) => s.trim(),
);
format("  hello  "); // "[HELLO]"
```

## API

| Function | Description |
|----------|-------------|
| `pipe(value, ...fns)` | Data-first pipeline, returns final result |
| `compose(...fns)` | Right-to-left function composition |
| `pipeline(...fns)` | Left-to-right composition, returns reusable function |
| `pipeAsync(value, ...fns)` | Async-aware pipeline with await between steps |


## Development

```bash
npm install
npm run build
npm test
```

## License

MIT
npm package: @philiprehberger/ts-promise-pool
Description: Concurrent promise execution with configurable pool size
Author: Philip Rehberger
Latest version: 0.1.5

# @philiprehberger/promise-pool

[![CI](https://github.com/philiprehberger/ts-promise-pool/actions/workflows/publish.yml/badge.svg)](https://github.com/philiprehberger/ts-promise-pool/actions/workflows/publish.yml)
[![npm version](https://img.shields.io/npm/v/@philiprehberger/promise-pool.svg)](https://www.npmjs.com/package/@philiprehberger/promise-pool)
[![License](https://img.shields.io/github/license/philiprehberger/ts-promise-pool)](LICENSE)

Concurrent promise execution with configurable pool size

## Installation

```bash
npm install @philiprehberger/promise-pool
```

## Usage

### Batch Execution

```ts
import { promisePool } from '@philiprehberger/promise-pool';

const tasks = urls.map(url => () => fetch(url).then(r => r.json()));

const { results, errors } = await promisePool(tasks, {
  concurrency: 5,
  onProgress: ({ completed, total, percent }) => {
    console.log(`${percent}% done (${completed}/${total})`);
  },
});
```

### Stop on Error

```ts
const { results, errors } = await promisePool(tasks, {
  concurrency: 3,
  stopOnError: true, // Stop scheduling new tasks after first failure
});
```

### Reusable Pool

```ts
import { createPool } from '@philiprehberger/promise-pool';

const pool = createPool({ concurrency: 3 });

// Tasks are queued and run with at most 3 concurrent
const result1 = pool.run(() => fetch('/api/1'));
const result2 = pool.run(() => fetch('/api/2'));
const result3 = pool.run(() => fetch('/api/3'));
const result4 = pool.run(() => fetch('/api/4')); // waits for a slot
```

## API

| Export | Description |
|--------|-------------|
| `promisePool(tasks, options?)` | Execute tasks with concurrency limit, returns `PoolResult` |
| `createPool(options?)` | Create a reusable pool with `run()` method |

### `PoolOptions`

| Option | Type | Default | Description |
|--------|------|---------|-------------|
| `concurrency` | `number` | `5` | Max concurrent tasks |
| `stopOnError` | `boolean` | `false` | Stop scheduling after first error |
| `onProgress` | `(progress) => void` | — | Progress callback |

### `PoolResult<T>`

| Property | Type | Description |
|----------|------|-------------|
| `results` | `(T \| undefined)[]` | Results in original order (`undefined` for failed tasks) |
| `errors` | `PoolError[]` | Array of `{ index, error }` for failed tasks |


## Development

```bash
npm install
npm run build
npm test
```

## License

MIT
npm package: @philiprehberger/ts-safe-json
Description: Safe JSON parsing and serialization with circular detection and depth limiting
Author: Philip Rehberger
Latest version: 0.1.4

# @philiprehberger/ts-safe-json

[![CI](https://github.com/philiprehberger/ts-safe-json/actions/workflows/publish.yml/badge.svg)](https://github.com/philiprehberger/ts-safe-json/actions/workflows/publish.yml)
[![npm version](https://img.shields.io/npm/v/@philiprehberger/ts-safe-json.svg)](https://www.npmjs.com/package/@philiprehberger/ts-safe-json)
[![License](https://img.shields.io/github/license/philiprehberger/ts-safe-json)](LICENSE)

Safe JSON parsing and serialization with circular detection and depth limiting.

## Installation

```bash
npm install @philiprehberger/ts-safe-json
```

## Usage

### Safe Parse

```ts
import { safeParse } from '@philiprehberger/ts-safe-json';

const { ok, data, error } = safeParse<User>(jsonString);
if (ok) {
  console.log(data.name); // typed as User
} else {
  console.error(error.message);
}
```

### Safe Stringify

```ts
import { safeStringify } from '@philiprehberger/ts-safe-json';

// Handles circular references
const obj: any = { name: 'test' };
obj.self = obj;
safeStringify(obj);
// {"name":"test","self":"[Circular]"}

// Depth limiting
safeStringify(deeplyNested, { maxDepth: 3 });
// Objects beyond depth 3 become "[MaxDepth]"

// Pretty print
safeStringify(data, { space: 2 });
```

## API

| Export | Description |
|--------|-------------|
| `safeParse<T>(input)` | Parse JSON string, returns `{ ok, data?, error? }` |
| `safeStringify(value, options?)` | Stringify with circular/depth protection |

### `SafeParseResult<T>`

| Property | Type | Description |
|----------|------|-------------|
| `ok` | `boolean` | Whether parsing succeeded |
| `data` | `T` | Parsed value (if ok) |
| `error` | `Error` | Parse error (if not ok) |

### `StringifyOptions`

| Option | Type | Default | Description |
|--------|------|---------|-------------|
| `maxDepth` | `number` | — | Max nesting depth before `"[MaxDepth]"` |
| `replacer` | `(key, value) => unknown` | — | Custom replacer function |
| `space` | `number` | — | Indentation spaces |


## Development

```bash
npm install
npm run build
npm test
```

## License

MIT
npm package: @philiprehberger/ts-safe-timeout
Description: Reliable timeout wrapper for async operations with AbortController support
Author: Philip Rehberger
Latest version: 0.1.4

# @philiprehberger/ts-safe-timeout

[![CI](https://github.com/philiprehberger/ts-safe-timeout/actions/workflows/publish.yml/badge.svg)](https://github.com/philiprehberger/ts-safe-timeout/actions/workflows/publish.yml)
[![npm version](https://img.shields.io/npm/v/@philiprehberger/ts-safe-timeout.svg)](https://www.npmjs.com/package/@philiprehberger/ts-safe-timeout)
[![License](https://img.shields.io/github/license/philiprehberger/ts-safe-timeout)](LICENSE)

Reliable timeout wrapper for async operations with AbortController support.

## Installation

```bash
npm install @philiprehberger/ts-safe-timeout
```

## Usage

### Timeout with Error

```ts
import { withTimeout, TimeoutError } from '@philiprehberger/ts-safe-timeout';

try {
  const data = await withTimeout(fetch('/api/data'), 5000);
} catch (error) {
  if (error instanceof TimeoutError) {
    console.log('Request timed out');
  }
}
```

### Timeout with Fallback

```ts
import { withTimeoutFallback } from '@philiprehberger/ts-safe-timeout';

const data = await withTimeoutFallback(fetch('/api/data'), 5000, cachedData);
// Returns cachedData if the fetch takes longer than 5 seconds
```

### Timeout Signal

```ts
import { createTimeoutSignal } from '@philiprehberger/ts-safe-timeout';

const signal = createTimeoutSignal(5000);
const response = await fetch('/api/data', { signal });
```

### With Options

```ts
const data = await withTimeout(fetch('/api'), 5000, {
  signal: existingAbortSignal,  // Also abort on external signal
  onTimeout: () => console.warn('Operation timed out'),
});
```

## API

| Export | Description |
|--------|-------------|
| `withTimeout(promise, ms, options?)` | Race promise against timeout, throws `TimeoutError` on timeout |
| `withTimeoutFallback(promise, ms, fallback)` | Returns fallback value on timeout instead of throwing |
| `createTimeoutSignal(ms)` | Creates an `AbortSignal` that aborts after `ms` |
| `TimeoutError` | Error class thrown on timeout, has `ms` property |

### `TimeoutOptions`

| Option | Type | Description |
|--------|------|-------------|
| `signal` | `AbortSignal` | External abort signal |
| `onTimeout` | `() => void` | Callback when timeout fires |


## Development

```bash
npm install
npm run build
npm test
```

## License

MIT
npm package: @philiprehberger/ts-schema-diff
Description: Compare JSON schemas and detect breaking changes
Author: Philip Rehberger
Latest version: 0.1.4

# @philiprehberger/ts-schema-diff

[![CI](https://github.com/philiprehberger/ts-schema-diff/actions/workflows/publish.yml/badge.svg)](https://github.com/philiprehberger/ts-schema-diff/actions/workflows/publish.yml)
[![npm version](https://img.shields.io/npm/v/@philiprehberger/ts-schema-diff.svg)](https://www.npmjs.com/package/@philiprehberger/ts-schema-diff)
[![License](https://img.shields.io/github/license/philiprehberger/ts-schema-diff)](LICENSE)

Compare JSON schemas and detect breaking changes.

## Installation

```bash
npm install @philiprehberger/ts-schema-diff
```

## Usage

```ts
import { diffSchemas } from '@philiprehberger/ts-schema-diff';

const changes = diffSchemas(
  { name: { type: 'string' }, email: { type: 'string' } },
  { name: { type: 'string' }, age: { type: 'number' } },
);

// [
//   { path: 'email', type: 'removed', breaking: true, message: 'Removed "email"' },
//   { path: 'age', type: 'added', breaking: false, message: 'Added "age"' },
// ]

const hasBreaking = changes.some(c => c.breaking);
```

## API

| Export | Description |
|--------|-------------|
| `diffSchemas(before, after)` | Deep-compare two schemas, returns `SchemaChange[]` |

### `SchemaChange`

| Property | Type | Description |
|----------|------|-------------|
| `path` | `string` | Dot-separated path |
| `type` | `'added' \| 'removed' \| 'changed'` | Change type |
| `breaking` | `boolean` | Whether the change is breaking |
| `before` | `unknown` | Previous value |
| `after` | `unknown` | New value |
| `message` | `string` | Human-readable description |


## Development

```bash
npm install
npm run build
npm test
```

## License

MIT
npm package: @philiprehberger/ts-typed-router
Description: Framework-agnostic typed URL routing with path parameter extraction
Author: Philip Rehberger
Latest version: 0.1.4

# @philiprehberger/ts-typed-router

[![CI](https://github.com/philiprehberger/ts-typed-router/actions/workflows/publish.yml/badge.svg)](https://github.com/philiprehberger/ts-typed-router/actions/workflows/publish.yml)
[![npm version](https://img.shields.io/npm/v/@philiprehberger/ts-typed-router.svg)](https://www.npmjs.com/package/@philiprehberger/ts-typed-router)
[![License](https://img.shields.io/github/license/philiprehberger/ts-typed-router)](LICENSE)

Framework-agnostic typed URL routing with path parameter extraction.

## Installation

```bash
npm install @philiprehberger/ts-typed-router
```

## Usage

```ts
import { createRouter } from '@philiprehberger/ts-typed-router';

const router = createRouter();

router.add('GET', '/users', () => listUsers());
router.add('GET', '/users/:id', (params) => getUser(params.id));
router.add('POST', '/users', () => createUser());
router.add('PUT', '/users/:id/roles/:role', (params) => setRole(params.id, params.role));

const match = router.match('GET', '/users/123');
if (match) {
  match.params; // { id: '123' }
  match.handler(match.params);
}

router.routes(); // list all registered routes
```

## API

| Export | Description |
|--------|-------------|
| `createRouter()` | Create a new router instance |

### `Router`

| Method | Description |
|--------|-------------|
| `add(method, path, handler)` | Register a route with `:param` placeholders |
| `match(method, url)` | Find matching route, returns `{ handler, params }` or `null` |
| `routes()` | List all registered routes |


## Development

```bash
npm install
npm run build
npm test
```

## License

MIT
npm package: @philiprehberger/typed-router
Description: Framework-agnostic typed URL routing with path parameter extraction
Author: Philip Rehberger
Latest version: 0.1.7

# @philiprehberger/typed-router

[![CI](https://github.com/philiprehberger/ts-typed-router/actions/workflows/ci.yml/badge.svg)](https://github.com/philiprehberger/ts-typed-router/actions/workflows/ci.yml)
[![npm version](https://img.shields.io/npm/v/@philiprehberger/typed-router.svg)](https://www.npmjs.com/package/@philiprehberger/typed-router)
[![Last updated](https://img.shields.io/github/last-commit/philiprehberger/ts-typed-router)](https://github.com/philiprehberger/ts-typed-router/commits/main)

Framework-agnostic typed URL routing with path parameter extraction

## Installation

```bash
npm install @philiprehberger/typed-router
```

## Usage

```ts
import { createRouter } from '@philiprehberger/typed-router';

const router = createRouter();

router.add('GET', '/users', () => listUsers());
router.add('GET', '/users/:id', (params) => getUser(params.id));
router.add('POST', '/users', () => createUser());
router.add('PUT', '/users/:id/roles/:role', (params) => setRole(params.id, params.role));

const match = router.match('GET', '/users/123');
if (match) {
  match.params; // { id: '123' }
  match.handler(match.params);
}

router.routes(); // list all registered routes
```

## API

| Export | Description |
|--------|-------------|
| `createRouter()` | Create a new router instance |

### `Router`

| Method | Description |
|--------|-------------|
| `add(method, path, handler)` | Register a route with `:param` placeholders |
| `match(method, url)` | Find matching route, returns `{ handler, params }` or `null` |
| `routes()` | List all registered routes |

## Development

```bash
npm install
npm run build
npm test
```

## Support

If you find this project useful:

⭐ [Star the repo](https://github.com/philiprehberger/ts-typed-router)

🐛 [Report issues](https://github.com/philiprehberger/ts-typed-router/issues?q=is%3Aissue+is%3Aopen+label%3Abug)

💡 [Suggest features](https://github.com/philiprehberger/ts-typed-router/issues?q=is%3Aissue+is%3Aopen+label%3Aenhancement)

❤️ [Sponsor development](https://github.com/sponsors/philiprehberger)

🌐 [All Open Source Projects](https://philiprehberger.com/open-source-packages)

💻 [GitHub Profile](https://github.com/philiprehberger)

🔗 [LinkedIn Profile](https://www.linkedin.com/in/philiprehberger)

## License

[MIT](LICENSE)
npm package: @philiprehberger/uid
Description: Collision-resistant, time-sortable, compact unique IDs in base62
Author: Philip Rehberger
Latest version: 0.1.2

# @philiprehberger/uid

[![CI](https://github.com/philiprehberger/ts-uid/actions/workflows/ci.yml/badge.svg)](https://github.com/philiprehberger/ts-uid/actions/workflows/ci.yml)
[![npm version](https://img.shields.io/npm/v/@philiprehberger/uid.svg)](https://www.npmjs.com/package/@philiprehberger/uid)
[![Last updated](https://img.shields.io/github/last-commit/philiprehberger/ts-uid)](https://github.com/philiprehberger/ts-uid/commits/main)

Collision-resistant, time-sortable, compact unique IDs in base62.

## Installation

```bash
npm install @philiprehberger/uid
```

## Usage

```ts
import { uid, extractTime, isValid } from '@philiprehberger/uid';

// Generate a 20-character sortable ID
const id = uid();
// => "0K8mT4x2RqNpLwZjHd3f"

// Custom length
const short = uid({ length: 12 });

// With prefix
const userId = uid({ prefix: 'usr_' });
// => "usr_0K8mT4x2RqNpLwZjHd3f"

// Extract embedded timestamp
const created = extractTime(id);
console.log(created); // Date object

// Validate format
isValid(id);     // true
isValid('abc');  // false
```

## API

### `uid(options?)`

Generate a unique, time-sortable base62 ID.

**Options:**

| Option | Type | Default | Description |
|--------|------|---------|-------------|
| `length` | `number` | `20` | Total length of the ID (excluding prefix) |
| `prefix` | `string` | `''` | String prepended to the ID |

Returns a `string` with 8 time-encoded characters followed by random base62 characters.

### `extractTime(id)`

Extract the embedded timestamp from an ID.

- **`id`** -- The ID string (with or without prefix)
- Returns a `Date` object

### `isValid(id)`

Check if a string matches the expected ID format.

- **`id`** -- The string to validate
- Returns `boolean`

## Development

```bash
npm install
npm run build
npm test
```

## Support

If you find this project useful:

⭐ [Star the repo](https://github.com/philiprehberger/ts-uid)

🐛 [Report issues](https://github.com/philiprehberger/ts-uid/issues?q=is%3Aissue+is%3Aopen+label%3Abug)

💡 [Suggest features](https://github.com/philiprehberger/ts-uid/issues?q=is%3Aissue+is%3Aopen+label%3Aenhancement)

❤️ [Sponsor development](https://github.com/sponsors/philiprehberger)

🌐 [All Open Source Projects](https://philiprehberger.com/open-source-packages)

💻 [GitHub Profile](https://github.com/philiprehberger)

🔗 [LinkedIn Profile](https://www.linkedin.com/in/philiprehberger)

## License

[MIT](LICENSE)