npm package: @seamos/bridge
Description: Type-safe bridge interface between SeamOS and WebView applications
Latest version: 0.1.2

# @seamos/bridge

React Native 앱과 WebView 간 타입 안전한 양방향 통신 브릿지.

## 설치

```bash
npm install @seamos/bridge
```

---

## WebView에서 사용하기

```typescript
import { bridge, BridgeEvent } from '@seamos/bridge/webview';
```

`bridge`는 싱글톤입니다.

---

## 데이터 접근 (settings / location)

`bridge.settings`와 `bridge.location`으로 현재 값을 동기적으로 읽을 수 있습니다.

```typescript
// NativeSettings & { location?: Location } | null
const settings = bridge.settings;
// settings.locale   — 'ko' | 'en' | 'de' | 'ja'
// settings.darkMode — boolean
// settings.fontSize — 'small' | 'medium' | 'large' | 'xlarge' | 'xxlarge' | 'xxxlarge'
// settings.location — Location | undefined

// Location | null
const location = bridge.location;
// location.latitude   — number
// location.longitude  — number
// location.altitude   — number | null | undefined
// location.accuracy   — number | null | undefined
// location.speed      — number | null | undefined
// location.heading    — number | null | undefined
// location.timestamp  — number (Unix ms)
```

> **초기값 주입에 대해**
> Native가 `injectedJavaScriptBeforeContentLoaded`로 `window.__SEAMOS_SETTINGS`를 주입한 경우, import 즉시 `bridge.settings`와 `bridge.location`을 사용할 수 있습니다. 그렇지 않으면 Native가 업데이트를 보낼 때까지 `null`입니다.

---

## 이벤트 구독

`bridge.addListener(event, listener)`로 Native에서 오는 업데이트를 구독합니다.
반환값은 구독 해제 함수입니다.

### Settings 업데이트

```typescript
const unsubscribe = bridge.addListener(
  BridgeEvent.SETTINGS_UPDATE,
  (settings) => {
    // settings: NativeSettings & { location?: Location }
  },
);

unsubscribe(); // 구독 해제
```

### Location 업데이트

```typescript
const unsubscribe = bridge.addListener(
  BridgeEvent.LOCATION_UPDATE,
  (location) => {
    // location: Location
  },
);

unsubscribe();
```

### 커스텀 메시지 수신

```typescript
const unsubscribe = bridge.addListener(
  BridgeEvent.custom('someAction'),
  (payload) => {
    // payload: unknown
  },
);

unsubscribe();
```

### 에러

```typescript
bridge.addListener(BridgeEvent.ERROR, (error) => {
  // error.code, error.message
});
```

| 에러                      | code               | 발생 조건                              |
| ------------------------- | ------------------ | -------------------------------------- |
| `BridgeVersionError`      | `VERSION_MISMATCH` | Native와 프로토콜 버전이 맞지 않을 때  |
| `BridgeNotAvailableError` | `NOT_AVAILABLE`    | `ReactNativeWebView`를 찾을 수 없을 때 |

---

## 메서드

### `bridge.sendCustom(action, payload)`

Native로 커스텀 메시지를 전송합니다.

```typescript
bridge.sendCustom('user:action', { itemId: 42 });
```

### `bridge.triggerHaptic(type)`

햅틱 피드백을 요청합니다.

```typescript
// type: 'light' | 'medium' | 'heavy' | 'success' | 'warning' | 'error' | 'selection'
bridge.triggerHaptic('success');
```

### `bridge.triggerVibration(options?)`

진동을 요청합니다.

```typescript
bridge.triggerVibration({ duration: 500 });
bridge.triggerVibration({ pattern: [0, 300, 100, 500] });
bridge.triggerVibration({ pattern: [0, 200, 100, 200], repeat: true });
```

> `duration`과 `pattern`은 함께 사용할 수 없습니다.

### `bridge.cancelVibration()`

반복 진동을 취소합니다.

```typescript
bridge.cancelVibration();
```

### `bridge.downloadFile(options)`

Native에 파일 다운로드(저장)를 요청합니다. Base64 인코딩된 파일 데이터를 전송합니다.

```typescript
bridge.downloadFile({
  data: 'dGVzdA==', // Base64 encoded file data
  fileName: 'report.pdf', // 파일명 (확장자 포함)
  mimeType: 'application/pdf', // MIME type
});
```

### 파일 다운로드 결과 수신

```typescript
const unsubscribe = bridge.addListener(
  BridgeEvent.FILE_DOWNLOAD_RESULT,
  (result) => {
    // result.fileName — 요청 시 전달한 파일명
    // result.success  — 저장 성공 여부
    // result.error    — 실패 시 에러 메시지
  },
);

unsubscribe();
```

---

## License

MIT