npm package: @khvostpola/pathy
Description: Type-safe path builder with automatic parameter extraction
Author: Khvost Pola
Latest version: 0.1.1

# @khvostpola/pathy

[![Khvost Pola](https://img.shields.io/badge/Khvost%20Pola-Open%20Source-blue)](https://github.com/khvostpola)
[![npm version](https://img.shields.io/npm/v/@khvostpola/pathy)](https://www.npmjs.com/package/@khvostpola/pathy)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)

Type-safe path builder with automatic parameter extraction for TypeScript.

## Features

- **Type-safe** — Automatic parameter extraction from path templates
- **Zero dependencies** — Lightweight and fast
- **Dual format** — ESM and CommonJS support
- **Flexible** — Support for required, optional, and rest parameters
- **RFC 3986** — Proper URL encoding

## Installation

```bash
npm install @khvostpola/pathy
```

```bash
yarn add @khvostpola/pathy
```

```bash
pnpm add @khvostpola/pathy
```

## Quick Start

```typescript
import { createPath } from '@khvostpola/pathy'

const userPath = createPath('/users/:id')

userPath({ path: { id: '123' } })
// → "/users/123"

// TypeScript knows 'id' is required!
userPath({ path: {} })
// ❌ Type error: Property 'id' is missing
```

## API

### `createPath(template, baseURL?)`

Creates a type-safe path builder function.

```typescript
const apiPath = createPath('/users/:id', 'https://api.example.com')
apiPath({ path: { id: '1' } })
// → "https://api.example.com/users/1"
```

### Parameter Syntax

| Syntax | Type | Example |
|--------|------|---------|
| `:param` | Required | `/users/:id` → `{ id: string }` |
| `:param?` | Optional | `/users/:id?` → `{ id?: string }` |
| `:param*` | Rest (array) | `/files/:path*` → `{ path: string \| string[] }` |

### `.withQuery<Q>()`

Adds typed query parameter support.

```typescript
type Filters = { page: number; sort?: string }

const listPath = createPath('/users').withQuery<Filters>()

listPath({ query: { page: 1, sort: 'name' } })
// → "/users?page=1&sort=name"
```

### `.template`

Access the original template string.

```typescript
const userPath = createPath('/users/:id')
console.log(userPath.template) // "/users/:id"
```

### `createPathsMap(paths)`

Groups multiple paths into an organized object.

```typescript
const api = createPathsMap({
  users: createPath('/users'),
  user: createPath('/users/:id'),
  userPosts: createPath('/users/:id/posts'),
})

api.users()                          // → "/users"
api.user({ path: { id: '5' } })      // → "/users/5"
```

### `createIndexedPathsMap(config)`

Builds hierarchical paths automatically.

```typescript
const paths = createIndexedPathsMap({
  index: 'api/v1',
  users: {
    index: 'users',
    detail: ':id',
    posts: ':id/posts',
  },
})

paths.users.detail({ path: { id: '42' } })  // → "/api/v1/users/42"
paths.users.posts({ path: { id: '42' } })   // → "/api/v1/users/42/posts"
```

## Advanced Examples

### Combining path and query params

```typescript
type UserQuery = { includeProfile?: boolean }

const userPath = createPath('/users/:id').withQuery<UserQuery>()

userPath({
  path: { id: '42' },
  query: { includeProfile: true },
})
// → "/users/42?includeProfile=true"
```

### Array query params

```typescript
type SearchQuery = { tags: string[] }

const searchPath = createPath('/search').withQuery<SearchQuery>()

searchPath({ query: { tags: ['typescript', 'nodejs'] } })
// → "/search?tags=typescript&tags=nodejs"
```

### BaseURL override

```typescript
const apiPath = createPath('/users/:id', 'https://api.prod.com')

// Override for specific call
apiPath({
  path: { id: '1' },
  baseURL: 'https://api.staging.com',
})
// → "https://api.staging.com/users/1"
```

### Rest parameters

```typescript
const filesPath = createPath('/files/:segments*')

filesPath({ path: { segments: ['folder', 'subfolder', 'file.txt'] } })
// → "/files/folder/subfolder/file.txt"
```

## TypeScript Support

The library provides full TypeScript support with automatic type inference:

```typescript
const userPath = createPath('/users/:id/:action?')

// TypeScript infers: { id: string; action?: string }
userPath({ path: { id: '123' } })           // ✓ OK
userPath({ path: { id: '123', action: 'edit' } }) // ✓ OK
userPath({ path: {} })                      // ✗ Error: 'id' is required
userPath({ path: { id: '123', foo: 'bar' } }) // ✗ Error: 'foo' doesn't exist
```

## License

[MIT](./LICENSE) © [Khvost Pola](https://github.com/khvostpola)

---

*"Aportar, crear, crecer... y tomar Pola."*