open-index/open-npm huggingface dataset:
npm package: @philiprehberger/invariant-ts
Description: Tiny runtime assertion with TypeScript narrowing
Author: Philip Rehberger
Latest version: 0.1.4

# @philiprehberger/invariant-ts

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

Tiny runtime assertion with TypeScript narrowing

## Installation

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

## Usage

```ts
import { invariant, assertNever, unreachable } from '@philiprehberger/invariant-ts';

const user = getUser(id);
invariant(user, 'User %s not found', id);
user.name; // narrowed to User

// Lazy message
invariant(data, () => `Expected data, got ${typeof data}`);

// Exhaustiveness checking
type Shape = 'circle' | 'square';
switch (shape) {
  case 'circle': return handleCircle();
  case 'square': return handleSquare();
  default: assertNever(shape);
}
```

### Custom Error Classes

```ts
import { invariantAs } from '@philiprehberger/invariant-ts';

class NotFoundError extends Error {
  constructor(message?: string) {
    super(message);
    this.name = 'NotFoundError';
  }
}

invariantAs(NotFoundError, user, 'User not found');
```

## API

| Function | Description |
|----------|-------------|
| `invariant(condition, message?, ...args)` | Assert condition is truthy, narrows type |
| `invariantAs(ErrorClass, condition, message?)` | Assert with custom error type |
| `assertNever(value)` | Exhaustiveness checking for switch/if chains |
| `unreachable(message?)` | Mark unreachable code paths |
| `InvariantError` | Error class thrown by `invariant()` |

## Development

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

## Support

If you find this project useful:

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

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

💡 [Suggest features](https://github.com/philiprehberger/invariant-ts/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/jwt-decode-ts
Description: Decode and inspect JWTs — no verification, typed payload, tiny
Author: Philip Rehberger
Latest version: 0.1.3

# @philiprehberger/jwt-decode-ts

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

Decode and inspect JWTs — no verification, typed payload, tiny

> **Note:** This library does **not** verify JWT signatures. It only decodes and inspects token contents. Always verify tokens server-side before trusting their claims.

## Installation

```bash
npm install @philiprehberger/jwt-decode-ts
```

## Usage

```ts
import {
  decodeJwt,
  decodePayload,
  isExpired,
  expiresIn,
  expiresAt,
  isJwt,
} from '@philiprehberger/jwt-decode-ts';

// Decode a full JWT
const { header, payload, signature } = decodeJwt(token);

// Extract just the payload
const claims = decodePayload(token);

// Check expiry
if (isExpired(token)) {
  console.log('Token has expired');
}

// Milliseconds until expiry (negative if already expired)
const ms = expiresIn(token);

// Get expiry as a Date
const date = expiresAt(token);

// Validate JWT format (type guard)
if (isJwt(unknownValue)) {
  const decoded = decodeJwt(unknownValue);
}
```

## API

| Function | Description |
| --- | --- |
| `decodeJwt<T>(token)` | Decode a JWT into `{ header, payload, signature }` |
| `decodePayload<T>(token)` | Decode and return only the payload |
| `isExpired(token, clockTolerance?)` | Check if the token's `exp` claim is in the past |
| `expiresIn(token)` | Milliseconds until expiry (negative if expired). Throws if no `exp` |
| `expiresAt(token)` | `Date` from the `exp` claim. Throws if no `exp` |
| `isJwt(value)` | Type guard — validates string has 3 non-empty base64url parts |
| `InvalidTokenError` | Custom error thrown for malformed tokens |

## 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/jwt-decode-ts)

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

💡 [Suggest features](https://github.com/philiprehberger/jwt-decode-ts/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/memo-map
Description: Memoization with WeakMap support for object keys, LRU eviction, and TTL
Author: Philip Rehberger
Latest version: 0.1.2

# @philiprehberger/memo-map

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

Memoization with WeakMap support for object keys, LRU eviction, and TTL.

## Installation

```bash
npm install @philiprehberger/memo-map
```

## Usage

```ts
import { memoize } from '@philiprehberger/memo-map';

// Basic memoization
const expensive = memoize((n: number) => {
  console.log('computing...');
  return n * n;
});

expensive(5); // computing... => 25
expensive(5); // => 25 (cached)

// With LRU eviction and TTL
const cached = memoize(fetchUser, {
  maxSize: 100,
  ttl: 60_000, // 1 minute
});

// Object args use WeakMap automatically
const getSize = memoize((obj: object) => Object.keys(obj).length);

// Custom key function
const byId = memoize(
  (user: { id: number; name: string }) => user.name.toUpperCase(),
  { key: (user) => user.id },
);
```

## API

### `memoize<T>(fn, options?)`

Wraps `fn` with memoization. Returns the memoized function with additional properties.

**Options:**

| Option | Type | Description |
|--------|------|-------------|
| `maxSize` | `number` | Maximum cache entries (LRU eviction) |
| `ttl` | `number` | Time-to-live in milliseconds |
| `key` | `(...args) => unknown` | Custom cache key function |

**Returned function extras:**

- **`.cache`** — The underlying `Map` instance
- **`.clear()`** — Clear all cached entries

**Key resolution (default):**

- Single primitive arg: used directly as key
- Single object arg: stored in a `WeakMap`
- Multiple args: `JSON.stringify(args)`

## Development

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

## Support

If you find this project useful:

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

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

💡 [Suggest features](https://github.com/philiprehberger/ts-memo-map/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/middleware-ts
Description: Framework-agnostic middleware composition engine
Author: Philip Rehberger
Latest version: 0.1.4

# @philiprehberger/middleware-ts

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

Framework-agnostic middleware composition engine

## Installation

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

## Usage

```ts
import { compose, createPipeline } from '@philiprehberger/middleware-ts';

type Ctx = { req: Request; user?: User };

const app = createPipeline<Ctx>()
  .use(logger)
  .useIf(requiresAuth, auth)
  .use(handler)
  .build();

await app({ req: new Request('/api') });
```

### Conditional Branching

```ts
import { branch } from '@philiprehberger/middleware-ts';

const authBranch = branch(
  (ctx) => ctx.req.url.startsWith('/api'),
  authMiddleware,
  publicMiddleware,
);
```

## API

| Function | Description |
|----------|-------------|
| `compose(...middlewares)` | Compose into single middleware |
| `createPipeline<Ctx>()` | Builder with `.use()` and `.useIf()` |
| `branch(condition, trueMw, falseMw?)` | Conditional middleware |
| `withErrorHandler(mw, handler)` | Wrap with error catching |

## Development

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

## Support

If you find this project useful:

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

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

💡 [Suggest features](https://github.com/philiprehberger/middleware-ts/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/money-ts
Description: Precise money and currency arithmetic
Author: Philip Rehberger
Latest version: 0.1.4

# @philiprehberger/money-ts

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

Precise money and currency arithmetic

## Installation

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

## Usage

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

const price = money(1099, 'USD');
const tax = price.percentage(8.5);
const total = price.add(tax);
total.format();        // "$11.92"
total.format('de-DE'); // "11,92 $"

const [a, b] = total.allocate([60, 40]); // handles remainders
```

### From Decimal

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

const price = money.fromDecimal(10.99, 'USD');
price.toCents(); // 1099
```

## API

| Method | Description |
|--------|-------------|
| `money(cents, currency)` | Create from minor units |
| `money.fromDecimal(decimal, currency)` | Create from decimal |
| `.add(other)` / `.subtract(other)` | Arithmetic (same currency) |
| `.multiply(n)` / `.divide(n)` / `.percentage(pct)` | Scaling |
| `.allocate(ratios)` | Split with remainder handling |
| `.format(locale?)` | Intl.NumberFormat currency string |
| `.toDecimal()` / `.toCents()` | Conversion |
| `.equals()` / `.greaterThan()` / `.lessThan()` | Comparison |

## Development

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

## Support

If you find this project useful:

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

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

💡 [Suggest features](https://github.com/philiprehberger/money-ts/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/natural-sort
Description: Natural string sorting — file2 before file10, not after
Author: Philip Rehberger
Latest version: 0.1.3

# @philiprehberger/natural-sort

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

Natural string sorting -- file2 before file10, not after.

## Installation

```bash
npm install @philiprehberger/natural-sort
```

## Usage

```ts
import { naturalSort, naturalCompare } from '@philiprehberger/natural-sort';

// Sort file names naturally
naturalSort(['file10', 'file2', 'file1', 'file20']);
// => ['file1', 'file2', 'file10', 'file20']

// Case insensitive
naturalSort(['Banana', 'apple', 'Cherry'], { caseInsensitive: true });
// => ['apple', 'Banana', 'Cherry']

// Descending
naturalSort(['v1', 'v10', 'v2'], { descending: true });
// => ['v10', 'v2', 'v1']

// Sort objects with accessor
const items = [{ name: 'item10' }, { name: 'item2' }, { name: 'item1' }];
naturalSort(items, { accessor: (i) => i.name });
// => [{ name: 'item1' }, { name: 'item2' }, { name: 'item10' }]

// Low-level comparison
naturalCompare('file2', 'file10'); // negative (file2 < file10)
```

## API

### `naturalSort<T>(array: T[], options?: NaturalSortOptions<T>): T[]`

Returns a new sorted array without mutating the original.

**Options:**

| Option            | Type               | Default | Description                    |
| ----------------- | ------------------ | ------- | ------------------------------ |
| `caseInsensitive` | `boolean`          | `false` | Ignore case when comparing     |
| `descending`      | `boolean`          | `false` | Sort in descending order       |
| `accessor`        | `(item: T) => string` | -    | Extract string from each item  |

### `naturalCompare(a: string, b: string, caseInsensitive?: boolean): number`

Compares two strings using natural ordering. Returns negative, zero, or positive.

## Development

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

## Support

If you find this project useful:

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

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

💡 [Suggest features](https://github.com/philiprehberger/ts-natural-sort/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/next-image-components
Description: Next.js Image wrappers with skeleton loading and error fallback
Author: Philip Rehberger
Latest version: 0.1.8

# @philiprehberger/next-image-components

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

Next.js Image wrappers with skeleton loading and error fallback

## Installation

```bash
npm install @philiprehberger/next-image-components clsx
```

## Usage

### `OptimizedImage`

Drop-in replacement for `next/image` with:
- Skeleton pulse animation while loading
- Fade-in transition on load
- Automatic fallback on error

```tsx
import { OptimizedImage } from '@philiprehberger/next-image-components';

<OptimizedImage
  src="/photos/hero.jpg"
  alt="Hero image"
  width={800}
  height={400}
  fallbackSrc="/images/placeholder.svg"
/>
```

## API

| Method | Description |
|--------|-------------|
| `OptimizedImage` | Next.js Image wrapper with skeleton loading animation, fade-in transition, and error fallback |

## Development

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

## Support

If you find this project useful:

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

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

💡 [Suggest features](https://github.com/philiprehberger/ts-next-image-components/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/next-layout-components
Description: Layout primitives for Next.js and React apps - Page wrapper and Card component
Author: Philip Rehberger
Latest version: 0.1.8

# @philiprehberger/next-layout-components

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

Layout primitives for Next.js and React apps

## Installation

```bash
npm install @philiprehberger/next-layout-components clsx
```

## Usage

```tsx
import { Page, Card } from '@philiprehberger/next-layout-components';

export default function HomePage() {
  return (
    <Page maxWidth="xl">
      <Card title="Welcome" padding="lg" hoverable>
        <p>Card content goes here</p>
      </Card>
    </Page>
  );
}
```

## API

### `Page`

Page wrapper with responsive padding and configurable max width.

| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `children` | `ReactNode` | -- | Page content |
| `maxWidth` | `'sm' \| 'md' \| 'lg' \| 'xl' \| '2xl' \| 'full'` | `'xl'` | Max width constraint |
| `className` | `string` | -- | Additional CSS classes |

### `Card`

Card container with optional title, configurable padding, and hover effect.

| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `children` | `ReactNode` | -- | Card content |
| `title` | `string` | -- | Optional card heading |
| `padding` | `'none' \| 'sm' \| 'md' \| 'lg'` | `'md'` | Padding size |
| `hoverable` | `boolean` | `false` | Enable hover effect |
| `className` | `string` | -- | Additional CSS classes |
| `contentWrapperClassName` | `string` | -- | CSS class for the content wrapper |

## Development

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

## Support

If you find this project useful:

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

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

💡 [Suggest features](https://github.com/philiprehberger/ts-next-layout-components/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/pick-omit
Description: Type-safe shallow and deep pick/omit for JavaScript objects
Author: Philip Rehberger
Latest version: 0.1.3

# @philiprehberger/pick-omit

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

Type-safe shallow and deep pick/omit for JavaScript objects.

## Installation

```bash
npm install @philiprehberger/pick-omit
```

## Usage

```ts
import { pick, omit, deepPick, deepOmit, pickBy, omitBy } from '@philiprehberger/pick-omit';

// Shallow pick/omit
pick({ a: 1, b: 2, c: 3 }, 'a', 'c');   // { a: 1, c: 3 }
omit({ a: 1, b: 2, c: 3 }, 'b');         // { a: 1, c: 3 }

// Deep pick/omit with dot notation
const user = { name: 'Alice', address: { city: 'NYC', zip: '10001' } };
deepPick(user, 'name', 'address.city');   // { name: 'Alice', address: { city: 'NYC' } }
deepOmit(user, 'address.zip');            // { name: 'Alice', address: { city: 'NYC' } }

// Predicate-based
pickBy({ a: 1, b: null, c: 'ok' }, (v) => v != null);  // { a: 1, c: 'ok' }
omitBy({ a: 1, b: null, c: 'ok' }, (v) => v == null);   // { a: 1, c: 'ok' }
```

## API

### `pick<T, K>(obj: T, ...keys: K[]): Pick<T, K>`

Returns a new object with only the specified keys. Type-safe with full inference.

### `omit<T, K>(obj: T, ...keys: K[]): Omit<T, K>`

Returns a new object without the specified keys.

### `deepPick(obj, ...paths): Record<string, unknown>`

Picks nested values using dot-notation paths (e.g., `'user.address.city'`).

### `deepOmit(obj, ...paths): Record<string, unknown>`

Removes nested values using dot-notation paths. Does not mutate the original.

### `pickBy<T>(obj: T, predicate: (value, key) => boolean): Partial<T>`

Picks entries where the predicate returns `true`.

### `omitBy<T>(obj: T, predicate: (value, key) => boolean): Partial<T>`

Omits entries where the predicate returns `true`.

## Development

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

## Support

If you find this project useful:

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

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

💡 [Suggest features](https://github.com/philiprehberger/ts-pick-omit/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/pipe
Description: Typed functional pipe and compose for TypeScript
Author: Philip Rehberger
Latest version: 0.1.5

# @philiprehberger/pipe

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

Typed functional pipe and compose for TypeScript

## Installation

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

## Usage

```ts
import { pipe, pipeline, pipeAsync } from '@philiprehberger/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/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
```

## Support

If you find this project useful:

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

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

💡 [Suggest features](https://github.com/philiprehberger/ts-pipe/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/predicate-ts
Description: Composable predicate builders for filtering and validation
Author: Philip Rehberger
Latest version: 0.1.4

# @philiprehberger/predicate-ts

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

Composable predicate builders for filtering and validation

## Installation

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

## Usage

```ts
import { is, where, and, or, not } from '@philiprehberger/predicate-ts';

users.filter(where({
  age: is.gte(18),
  status: is.in(['active', 'verified']),
}));

const isSpecial = or(
  is.property('role', is.equal('admin')),
  and(is.property('tenure', is.gt(5)), is.property('rating', is.gte(4.5))),
);
```

## API

| Function | Description |
|----------|-------------|
| `is.equal(v)` | Strict equality |
| `is.gt(n)` / `is.gte(n)` / `is.lt(n)` / `is.lte(n)` | Numeric comparisons |
| `is.between(a, b)` | Inclusive range |
| `is.match(regex)` | Regex test |
| `is.startsWith(s)` / `is.endsWith(s)` / `is.includes(s)` | String checks |
| `is.in(values)` | Value in array |
| `is.property(key, predicate)` | Check object property |
| `and(...predicates)` | All must pass |
| `or(...predicates)` | Any must pass |
| `not(predicate)` | Invert predicate |
| `where(shape)` | Object shape predicate |

## Development

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

## Support

If you find this project useful:

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

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

💡 [Suggest features](https://github.com/philiprehberger/predicate-ts/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/pretty-ms
Description: Convert milliseconds to human-readable strings and back
Author: Philip Rehberger
Latest version: 0.1.2

# @philiprehberger/pretty-ms

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

Convert milliseconds to human-readable strings and back.

## Installation

```bash
npm install @philiprehberger/pretty-ms
```

## Usage

```ts
import { prettyMs, parseMs } from '@philiprehberger/pretty-ms';

prettyMs(9015000);
// => "2h 30m 15s"

prettyMs(9015000, { compact: true });
// => "2h"

prettyMs(9015000, { verbose: true });
// => "2 hours 30 minutes 15 seconds"

prettyMs(9015000, { colonNotation: true });
// => "2:30:15"

prettyMs(9015000, { unitCount: 2 });
// => "2h 30m"

parseMs('2h 30m 15s');
// => 9015000

parseMs('2:30:15');
// => 9015000
```

## API

### `prettyMs(ms: number, options?: PrettyMsOptions): string`

Convert milliseconds to a human-readable string.

**Options:**

| Option | Type | Default | Description |
|---|---|---|---|
| `compact` | `boolean` | `false` | Show only the most significant unit |
| `verbose` | `boolean` | `false` | Use full unit names |
| `colonNotation` | `boolean` | `false` | Use colon notation (H:MM:SS) |
| `unitCount` | `number` | `undefined` | Limit to N most significant units |

### `parseMs(str: string): number`

Parse a human-readable duration string back to milliseconds. Supports unit notation (`2h 30m 15s`) and colon notation (`2:30:15`).

## Development

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

## Support

If you find this project useful:

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

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

💡 [Suggest features](https://github.com/philiprehberger/ts-pretty-ms/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/query-string-ts
Description: Type-safe URL query string parsing and building
Author: Philip Rehberger
Latest version: 0.1.4

# @philiprehberger/query-string-ts

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

Type-safe URL query string parsing and building

## Installation

```bash
npm install @philiprehberger/query-string-ts
```

## Usage

```ts
import { parseQuery, buildQuery, appendQuery } from '@philiprehberger/query-string-ts';

const params = parseQuery('?page=2&tags=a&tags=b', {
  page: 'number',
  tags: 'string[]',
});
// { page: 2, tags: ['a', 'b'] } — typed

buildQuery({ page: 2, tags: ['a', 'b'] });           // "page=2&tags=a&tags=b"
appendQuery('https://example.com?a=1', { b: '2' });  // "https://example.com?a=1&b=2"
```

### Array Formats

```ts
buildQuery({ tags: ['a', 'b'] }, { arrayFormat: 'comma' });    // "tags=a,b"
buildQuery({ tags: ['a', 'b'] }, { arrayFormat: 'brackets' }); // "tags[]=a&tags[]=b"
```

## API

| Function | Description |
|----------|-------------|
| `parseQuery(search, schema?)` | Parse with optional type coercion |
| `buildQuery(params, options?)` | Serialize object to query string |
| `appendQuery(url, params)` | Append params to existing URL |
| `pickQuery(search, keys)` | Extract subset of params |
| `omitQuery(search, keys)` | Remove params by key |

## Development

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

## Support

If you find this project useful:

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

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

💡 [Suggest features](https://github.com/philiprehberger/query-string-ts/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/react-ui-primitives
Description: Lightweight React UI primitives - Button, Alert, and more
Author: Philip Rehberger
Latest version: 0.1.8

# @philiprehberger/react-ui-primitives

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

Lightweight React UI primitives with Tailwind CSS styling

## Installation

```bash
npm install @philiprehberger/react-ui-primitives clsx
```

## Usage

### `Button`

Multi-variant button with loading state

```tsx
import { Button } from '@philiprehberger/react-ui-primitives';

<Button variant="primary" onClick={handleClick}>Save</Button>
<Button variant="danger" loading>Deleting...</Button>
```

Variants: `primary`, `success`, `danger`, `secondary`

### `Alert`

Alert banner with type-based styling and optional action button.

```tsx
import { Alert } from '@philiprehberger/react-ui-primitives';

<Alert type="success" message="Changes saved successfully" />
<Alert type="error" message="Something went wrong" onAction={retry} actionText="Retry" />
```

Types: `error`, `success`, `warning`, `info`

## API

| Export | Type | Description |
|--------|------|-------------|
| `Button` | Component | Multi-variant button with loading state and hover text |
| `Alert` | Component | Alert banner with type-based styling and optional action button |
| `ButtonProps` | Type | Props: `variant?`, `loading?`, `hoverText?`, plus native button attrs |
| `ButtonVariant` | Type | `'primary' \| 'success' \| 'danger' \| 'secondary'` |
| `AlertProps` | Type | Props: `type`, `message`, `onAction?`, `actionText?`, `className?` |
| `AlertType` | Type | `'error' \| 'success' \| 'warning' \| 'info'` |

## Development

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

## Support

If you find this project useful:

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

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

💡 [Suggest features](https://github.com/philiprehberger/ts-react-ui-primitives/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/safe-regex
Description: Validate and sanitize regular expressions to prevent ReDoS attacks
Author: Philip Rehberger
Latest version: 0.1.2

# @philiprehberger/safe-regex

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

Validate and sanitize regular expressions to prevent ReDoS attacks.

## Installation

```bash
npm install @philiprehberger/safe-regex
```

## Usage

```ts
import { isSafe, safeParse, escape } from '@philiprehberger/safe-regex';

isSafe('abc');
// => true

isSafe('(a+)+');
// => false (nested quantifier — catastrophic backtracking)

const regex = safeParse('abc', 'i');
// => /abc/i

const unsafe = safeParse('(a+)+');
// => null

escape('hello.world');
// => "hello\\.world"

escape('a+b*c?');
// => "a\\+b\\*c\\?"
```

## API

### `isSafe(input: string | RegExp): boolean`

Check whether a regular expression is safe from ReDoS attacks. Detects nested quantifiers and overlapping alternations that cause catastrophic backtracking.

### `safeParse(str: string, flags?: string): RegExp | null`

Compile a regex string into a `RegExp` only if it passes safety checks. Returns `null` if the pattern is unsafe or invalid.

### `escape(str: string): string`

Escape all regex special characters in a string so it can be used as a literal match: `. * + ? ^ $ { } ( ) | [ ] \`

## Development

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

## Support

If you find this project useful:

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

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

💡 [Suggest features](https://github.com/philiprehberger/ts-safe-regex/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/schema-diff
Description: Compare JSON schemas and detect breaking changes
Author: Philip Rehberger
Latest version: 0.1.7

# @philiprehberger/schema-diff

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

Compare JSON schemas and detect breaking changes

## Installation

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

## Usage

```ts
import { diffSchemas } from '@philiprehberger/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
```

## Support

If you find this project useful:

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

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

💡 [Suggest features](https://github.com/philiprehberger/ts-schema-diff/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/semver-ts
Description: Lightweight semver parsing, comparison, and range matching
Author: Philip Rehberger
Latest version: 0.1.4

# @philiprehberger/semver-ts

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

Lightweight semver parsing, comparison, and range matching

## Installation

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

## Usage

```ts
import { parse, satisfies, sort, increment } from '@philiprehberger/semver-ts';

parse('2.1.0-rc.1');
// { major: 2, minor: 1, patch: 0, prerelease: ['rc', 1] }

satisfies('1.5.0', '^1.0.0');    // true
sort(['2.0.0', '1.0.0', '1.5.0']); // ['1.0.0', '1.5.0', '2.0.0']
increment('1.2.3', 'minor');     // '1.3.0'
```

## API

| Function | Description |
|----------|-------------|
| `parse(version)` | Parse to SemVer object |
| `coerce(input)` | Loose parse (e.g. "v1.2") |
| `compare(a, b)` | Compare two versions |
| `gt` / `lt` / `gte` / `lte` / `eq` | Boolean comparisons |
| `satisfies(version, range)` | Check range match |
| `maxSatisfying(versions, range)` | Highest matching version |
| `minSatisfying(versions, range)` | Lowest matching version |
| `increment(version, type)` | Bump version |
| `sort(versions)` / `rsort(versions)` | Sort versions |

## Development

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

## Support

If you find this project useful:

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

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

💡 [Suggest features](https://github.com/philiprehberger/semver-ts/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/task-runner
Description: Task orchestration with dependency resolution and topological execution
Author: Philip Rehberger
Latest version: 0.1.2

# @philiprehberger/task-runner

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

Task orchestration with dependency resolution and topological execution.

## Installation

```bash
npm install @philiprehberger/task-runner
```

## Usage

```ts
import { createRunner } from '@philiprehberger/task-runner';

const runner = createRunner({
  onStart: (name) => console.log(`Starting: ${name}`),
  onComplete: (name) => console.log(`Done: ${name}`),
});

runner.task('compile', async () => {
  await compileSource();
}, ['lint']);

runner.task('lint', async () => {
  await runLinter();
});

runner.task('test', async () => {
  await runTests();
}, ['compile']);

// Preview execution order
console.log(runner.dryRun('test')); // ['lint', 'compile', 'test']

// Execute with dependency resolution
await runner.run('test');
```

## API

### `createRunner(options?): Runner`

Creates a new task runner.

#### `RunnerOptions`

- **`onStart?(name)`** — Called when a task begins
- **`onComplete?(name)`** — Called when a task finishes
- **`onError?(name, error)`** — Called when a task fails

#### `Runner`

- **`task(name, fn, deps?)`** — Register a task with optional dependencies
- **`run(name?)`** — Execute a task and its dependencies, or all tasks if no name given
- **`dryRun(name?)`** — Return execution order as `string[]` without executing

## Development

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

## Support

If you find this project useful:

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

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

💡 [Suggest features](https://github.com/philiprehberger/ts-task-runner/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)