<!-- source-hash: 05189ee6bb31560bd75b396ab69f4552 -->
Provides URL state management for REST APIs by syncing typed parameters to/from URL search params without requiring GraphQL schema introspection.

## Key Components

### Types & Interfaces
- **`ParamConfig<T>`** — Defines type, default value, and required flag for a single URL parameter
- **`ParamSchema`** — Record mapping parameter names to their `ParamConfig`
- **`InferParamsFromSchema<TSchema>`** — Utility type that infers the typed output shape from a schema
- **`InferInputParamsFromSchema<TSchema>`** — More permissive input variant allowing `null`/`undefined`
- **`UseApiParamsReturn<TSchema>`** — Full return type including `params`, `setParam`, `setParams`, `clearParams`, `resetParams`, and `urlSearchParams`

### Functions
- **`defineParamSchema<T>`** — Identity helper that preserves literal types on schema definitions
- **`useApiParams<TSchema>`** — Primary hook; reads, coerces, and syncs URL search params using a manual schema
- **`useContentStable<T>`** *(internal)* — Stabilizes object references across renders using JSON key comparison
- **`reuseIfShallowEqual<T>`** *(internal)* — Prevents array reference churn when content is unchanged

## Usage Example

```typescript
import { defineParamSchema, useApiParams } from './use-api-params'

const schema = defineParamSchema({
  search: { type: 'string', default: '' },
  page:   { type: 'number', default: 1 },
  tags:   { type: 'array',  default: [] },
})

function ProductList() {
  const { params, setParam, setParams, urlSearchParams } = useApiParams(schema)

  // params is fully typed: { search: string; page: number; tags: string[] }
  useEffect(() => {
    fetch(`/api/products?${urlSearchParams}`)
  }, [urlSearchParams])

  return (
    <input
      value={params.search}
      onChange={e => setParam('search', e.target.value)}
    />
  )
}
```

> **Reference stability:** Internal memoization ensures `params`, array fields, and setter callbacks keep stable references across renders unless the URL or schema content actually changes — safe to use directly in `useEffect` dependency arrays.