npm package: @sumi137/native
Description: Cross-platform native modules for roze (node + react-native)
Latest version: 1.0.3

# @sumi137/native

Cross-platform native module bindings for Node.js and React Native.

## Installation

```bash
npm install @sumi137/native
# or
yarn add @sumi137/native
```

## Metro configuration

Add the following blockList to your `metro.config.js` to prevent Metro from
trying to bundle the `.node.ts` (Node.js-only) implementation files:

```js
const { getDefaultConfig } = require('expo/metro-config');

const config = getDefaultConfig(__dirname);

module.exports = {
    ...config,
    resolver: {
        ...config.resolver,
        blockList: [/(\/node_modules\/@sumi137\/native\/.+?\/.+?\.node\.ts)$/]
    }
};
```

## tsconfig paths

Add the following to your `tsconfig.json` to resolve `@native/*` imports:

```json
{
    "compilerOptions": {
        "paths": {
            "@native/*": ["./node_modules/@sumi137/native/*"]
        }
    }
}
```

## Usage

Initialize all native modules at startup:

```typescript
import { load_native_modules } from "@sumi137/native";

await load_native_modules();
```

Or load individual modules as needed:

```typescript
import { load_native_fs, fs } from "@sumi137/native/fs/fs";
import { load_native_zip, zip } from "@sumi137/native/zip/zip";

await load_native_fs();
await load_native_zip();
```

### File System

```typescript
import { fs } from "@sumi137/native/fs/fs";

const contents = await fs().read_as_string("/path/to/file.txt", { encoding: "utf8" });
await fs().write_file_as_string("/path/to/output.txt", "hello", { encoding: "utf8" });

const files = await fs().read_directory("/some/dir");
const info = await fs().get_info("/path/to/file.txt");

await fs().copy("/src.txt", "/dst.txt", {});
await fs().move("/old.txt", "/new.txt", {});
await fs().remove("/path/to/file.txt");

const downloaded = await fs().download_to_file("https://example.com/file.zip");
```

### Zip

```typescript
import { zip } from "@sumi137/native/zip/zip";

const entries = await zip().list_entries("archive.zip");
const buffer = await zip().stream_entry("archive.zip", "path/inside/archive.txt");
await zip().extract_all("archive.zip", "./output/");
await zip().create_zip("./source_dir/", "output.zip");
```

### FFmpeg / FFprobe

```typescript
import { ffmpeg } from "@sumi137/native/ffmpeg/ffmpeg";
import { ffprobe } from "@sumi137/native/ffprobe/ffprobe";

const result = await ffmpeg().execute_args(
    ["-i", "input.mp3", "-ac", "1", "output.wav"],
    (stats) => console.log(\`Progress: \${stats.time_seconds}s, speed: \${stats.speed}x\`)
);
await result.retcode; // 0 = success

const probe = await ffprobe().execute_args(["-i", "input.mp3", "-show_format", "-print_format", "json"]);
```

### Voice Synth

```typescript
import { load_native_voice_synth, voice_synth } from "@sumi137/native/voice_synth/voice_synth";

await load_native_voice_synth(); // not included in load_native_modules()

const voices = await voice_synth().get_voices();
await voice_synth().speak("Hello world", { voice_bank: voices[0], rate: 1.0 });

// Export speech to files
await voice_synth().speak_export(
    [{ text: "Chapter one.", export_path: "/tmp/ch1.wav" }],
    { voice_bank: voices[0] }
);
```

### Audio Duration

```typescript
import { get_audio_duration } from "@sumi137/native/get_audio_duration/get_audio_duration";

const seconds = await get_audio_duration().get_audio_duration("/path/to/audio.mp3");
```

### MMKV (Key-Value Storage)

```typescript
import { mmkv } from "@sumi137/native/mmkv/mmkv";

await mmkv().load_mmkv({ id: "my-store" });

await mmkv().set_string("key", "value");
const val = await mmkv().get_string("key"); // "value" | undefined

await mmkv().set_boolean("flag", true);
await mmkv().set_number("count", 42);

const keys = await mmkv().get_keys();
await mmkv().remove_key("key");
```

### Document Picker (React Native only)

```typescript
import { document_picker } from "@sumi137/native/document_picker/document_picker";

const file = await document_picker().pick_file();
if (!("error" in file)) {
    console.log(file.name, file.extension, file.size_bytes);
}

const dir = await document_picker().pick_directory();
```

## Modules

| Module | Description |
|--------|-------------|
| \`fs\` | Cross-platform file system operations |
| \`zip\` | ZIP archive read/write/extract |
| \`ffmpeg\` | FFmpeg execution with progress callbacks |
| \`ffprobe\` | FFprobe media inspection |
| \`voice_synth\` | Text-to-speech synthesis (loaded separately) |
| \`get_audio_duration\` | Audio duration extraction |
| \`mmkv\` | High-performance key-value storage |
| \`document_picker\` | File/directory picker (React Native) |
| \`miscnative\` | Misc native ops (e.g. keep screen awake) |
| \`potoken\` | YouTube PoToken generation |
| \`sabr_downloader\` | YouTube SABR protocol downloader |
| \`jseval\` | JavaScript evaluation in WebView context |