npm package: @helvaddress/sdk
Description: Official JavaScript/TypeScript SDK for HelvAddress Swiss address autocomplete API
Author: HelvAddress
Latest version: 0.1.0

# @helvaddress/sdk

Official JavaScript/TypeScript SDK for the [HelvAddress](https://www.helvaddress.ch) Swiss address autocomplete API.

[![CI](https://github.com/helvaddress/helvaddress-js-sdk/actions/workflows/ci.yml/badge.svg)](https://github.com/helvaddress/helvaddress-js-sdk/actions/workflows/ci.yml)
[![npm](https://img.shields.io/npm/v/@helvaddress/sdk)](https://www.npmjs.com/package/@helvaddress/sdk)

## Features

- 🏔️ Swiss address autocomplete powered by official GWR/RegBL data
- 🔒 **Confidential** — queries are sent via POST body, never exposed in URL logs
- 🧩 **Decoupled architecture** — headless API client + optional UI widget
- 📦 Tree-shakable ESM & CJS builds with full TypeScript types
- 🎨 Customizable UI with built-in styles or bring-your-own CSS
- ⌨️ Full keyboard navigation & accessibility (ARIA)
- 🪶 Zero dependencies — just the native `fetch` API

## Installation

```bash
yarn add @helvaddress/sdk
```

## Quick Start

### Option 1: UI Widget (drop-in autocomplete)

```html
<div id="address-input"></div>
```

```ts
import { HelvAddressAutocomplete } from '@helvaddress/sdk';

const widget = new HelvAddressAutocomplete({
  apiKey: 'your-jwt-token',
  container: '#address-input',
  maxSuggestions: 5,
  value: 'object',
  onSelect: (value, address) => {
    console.log('Selected address:', address);
    console.log('EGAID:', address.id);
  },
});
```

### Option 2: Headless Client (for custom UI / Node.js)

```ts
import { HelvAddressClient } from '@helvaddress/sdk';

const client = new HelvAddressClient({ apiKey: 'your-jwt-token' });

const results = await client.autocomplete({
  query: 'Bahnhofstrasse Zürich',
  limit: 5,
});

console.log(results.content);
// [{ id: 100001234, streetName: 'Bahnhofstrasse', houseNumber: '1', zip4: 8001, ... }]
```

## Architecture

The SDK is split into two independent layers:

```
┌─────────────────────────────────────────────┐
│            HelvAddressAutocomplete          │  ← UI layer (DOM)
│  (textbox + dropdown + keyboard navigation) │
├─────────────────────────────────────────────┤
│              HelvAddressClient              │  ← Core layer (headless)
│  (API calls, auth, error handling)          │
└─────────────────────────────────────────────┘
```

- **`HelvAddressClient`** — Pure API client with zero DOM dependencies. Use it in Node.js, React, Vue, Svelte, or any framework.
- **`HelvAddressAutocomplete`** — Vanilla JS UI widget that uses the client internally. Drop it into any HTML page.

## Widget Options

| Option | Type | Default | Description |
|---|---|---|---|
| `apiKey` | `string` | *required* | JWT bearer token |
| `container` | `HTMLElement \| string` | *required* | DOM element or CSS selector |
| `value` | `'id' \| 'object' \| 'formatted'` | `'object'` | What `onSelect` returns |
| `maxSuggestions` | `number` (1–20) | `5` | Max dropdown items |
| `threshold` | `number` (0–1) | `0.3` | Min relevance score |
| `minChars` | `number` | `2` | Min chars before querying |
| `debounceMs` | `number` | `250` | Debounce delay (ms) |
| `placeholder` | `string` | `'Search Swiss address...'` | Input placeholder |
| `className` | `string` | — | Extra CSS class on root |
| `noStyle` | `boolean` | `false` | Disable built-in styles |
| `entityStatus` | `'REAL' \| 'PLANNED' \| 'OUTDATED'` | `'REAL'` | Address status filter |
| `addressCategories` | `AddressCategory[]` | — | Category filter |
| `renderSuggestion` | `(addr) => string` | — | Custom suggestion HTML |
| `formatValue` | `(addr) => string` | — | Custom input display |

### Callbacks

| Callback | Signature | Description |
|---|---|---|
| `onSelect` | `(value, address) => void` | User selects an address |
| `onResults` | `(results) => void` | New results received |
| `onError` | `(error) => void` | API error occurred |
| `onChange` | `(query) => void` | Input value changed |
| `onFocus` | `() => void` | Input focused |
| `onBlur` | `() => void` | Input blurred |
| `onOpen` | `(open) => void` | Dropdown opened/closed |
| `onClear` | `() => void` | Input cleared |

### Widget Methods

```ts
widget.setValue('Bahnhofstrasse 1');  // Set input value
widget.getValue();                     // Get current value
widget.clear();                        // Clear input & close
widget.open();                         // Open dropdown
widget.close();                        // Close dropdown
widget.focus();                        // Focus input
widget.getInputElement();              // Get <input> DOM element
widget.destroy();                      // Remove & clean up
```

## Selection Value Types

Control what `onSelect` receives with the `value` option:

```ts
// value: 'id' → returns EGAID number
onSelect: (id: number) => { ... }

// value: 'object' → returns full AddressResult
onSelect: (address: AddressResult) => {
  console.log(address.streetName, address.zip4);
}

// value: 'formatted' → returns formatted string
onSelect: (formatted: string) => {
  // "Bahnhofstrasse 1, 8001 Zürich"
}
```

## Custom Styling

Disable built-in styles and use your own CSS:

```ts
const widget = new HelvAddressAutocomplete({
  apiKey: 'your-token',
  container: '#input',
  noStyle: true,
});
```

All elements use BEM-style class names prefixed with `ha-autocomplete`:

| Class | Element |
|---|---|
| `.ha-autocomplete` | Root container |
| `.ha-autocomplete-input` | Text input |
| `.ha-autocomplete-dropdown` | Dropdown panel |
| `.ha-autocomplete-option` | Single suggestion |
| `.ha-autocomplete-option--active` | Highlighted suggestion |
| `.ha-autocomplete-clear` | Clear button |
| `.ha-autocomplete-empty` | "No results" message |
| `.ha-autocomplete-loading` | Loading indicator |
| `.ha-autocomplete-error` | Error message |

## AddressResult Type

```ts
interface AddressResult {
  id: number;           // EGAID (unique building entrance ID)
  streetName: string;   // Official street name
  houseNumber: string;  // Building number
  zip4: number;         // 4-digit postal code (PLZ/NPA)
  zipa: number;         // Additional postal digits (00–99)
  locality: string;     // Postal locality name
  municipality: string; // Political municipality
  score: number;        // Relevance score (0.0–1.0)
}
```

## Error Handling

```ts
import { HelvAddressClient, HelvAddressError } from '@helvaddress/sdk';

try {
  const results = await client.autocomplete({ query: 'test' });
} catch (err) {
  if (err instanceof HelvAddressError) {
    console.error(`API Error ${err.status}: ${err.message}`);
    console.error('Details:', err.apiError?.validationErrors);
  }
}
```

## Browser Support

Works in all modern browsers with native `fetch` support (Chrome, Firefox, Safari, Edge).  
For older browsers, provide a `fetch` polyfill:

```ts
import fetch from 'cross-fetch';

const client = new HelvAddressClient({
  apiKey: 'token',
  fetch,
});
```

## Development

```bash
yarn install       # Install dependencies
yarn build         # Build ESM + CJS
yarn test          # Run tests
yarn typecheck     # Type checking
yarn dev           # Watch mode
```

## License

MIT © [HelvAddress](https://www.helvaddress.ch)