npm package: @flutterpilot/core
Description: Core TypeScript library for connecting to and interacting with Flutter debug apps
Author: FlutterPilot
Latest version: 0.1.0

# @flutterpilot/core

TypeScript client for connecting to and interacting with Flutter apps running in debug mode. Provides a high-level API over the Dart VM service protocol and the `flutter_pilot` service extensions.

## Installation

```bash
npm install @flutterpilot/core
```

## Quick Start

```typescript
import { FlutterPilotClient } from '@flutterpilot/core';

const client = await FlutterPilotClient.connect('ws://127.0.0.1:PORT/TOKEN=/ws');
const screenshot = await client.screenshot(); // PNG Buffer
const tree = await client.getWidgetTree();
await client.tap({ findBy: 'text', value: 'Increment' });
await client.disconnect();
```

## API Reference

### Connection

#### `FlutterPilotClient.connect(vmServiceUri: string): Promise<FlutterPilotClient>`

Static factory method. Connects to a running Flutter app via its Dart VM service WebSocket URI, discovers the Flutter isolate, and checks for `flutter_pilot` extensions.

```typescript
const client = await FlutterPilotClient.connect('ws://127.0.0.1:56789/ABCDEF=/ws');
```

#### `client.disconnect(): Promise<void>`

Closes the WebSocket connection and prevents automatic reconnection.

#### `client.connected: boolean`

Returns `true` if the WebSocket connection is currently open.

#### `client.hasFlutterPilotExtension: boolean`

Returns `true` if the connected app has the `flutter_pilot` Dart package installed and its extensions are registered. Some methods (screenshot, widget tree, hot reload) work without the extension; interaction methods (tap, scroll, enterText) require it.

---

### Inspection

#### `client.screenshot(): Promise<Buffer>`

Captures a screenshot of the running Flutter app. Returns a PNG image as a Node.js `Buffer`.

```typescript
import { writeFileSync } from 'fs';
const png = await client.screenshot();
writeFileSync('screenshot.png', png);
```

#### `client.screenshotAsBase64(): Promise<string>`

Same as `screenshot()` but returns the PNG as a base64-encoded string. Useful when you need to embed the image in JSON or pass it to an API.

```typescript
const base64 = await client.screenshotAsBase64();
```

#### `client.getWidgetTree(opts?: { maxDepth?: number }): Promise<WidgetNode>`

Returns the widget tree using Flutter's built-in inspector. Does not require the `flutter_pilot` Dart package.

```typescript
const tree = await client.getWidgetTree({ maxDepth: 5 });
// { id, type, description, children: [...] }
```

#### `client.getSemanticsTree(maxDepth?: number): Promise<SemanticsNode>`

Returns the accessibility/semantics tree as compact JSON. Each node includes only non-empty fields (`id`, `label`, `value`, `hint`, `actions`, `flags`, `rect`, `children`). Requires the `flutter_pilot` Dart package.

```typescript
const semantics = await client.getSemanticsTree(10);
```

#### `client.getWidgetDetails(widgetId: string): Promise<Record<string, unknown>>`

Returns detailed properties for a specific widget by its inspector object ID.

```typescript
const details = await client.getWidgetDetails('inspector-42');
```

#### `client.getInteractiveWidgets(): Promise<InteractiveWidget[]>`

Lists all interactive widgets currently on screen (buttons, text fields, sliders, checkboxes, etc.) with their type, label, and bounding box. Requires the `flutter_pilot` Dart package.

```typescript
const widgets = await client.getInteractiveWidgets();
// [{ widgetType: 'FloatingActionButton', label: '+', centerX: 195, centerY: 680, ... }]
```

---

### Interaction

All interaction methods require the `flutter_pilot` Dart package to be installed in the target app.

#### `client.tap(target: TapTarget | FinderTarget): Promise<void>`

Taps a widget. Accepts either absolute coordinates or a finder query.

```typescript
// Tap by coordinates
await client.tap({ x: 195, y: 680 });

// Tap by text
await client.tap({ findBy: 'text', value: 'Submit' });

// Tap by key
await client.tap({ findBy: 'key', value: 'login_button' });

// Tap by widget type
await client.tap({ findBy: 'type', value: 'FloatingActionButton' });

// Tap by semantics label
await client.tap({ findBy: 'semantics', value: 'Add item' });
```

#### `client.enterText(text: string): Promise<void>`

Enters text into the currently focused text field. Tap a `TextField` first to focus it.

```typescript
await client.tap({ findBy: 'type', value: 'TextField' });
await client.enterText('hello@example.com');
```

#### `client.scroll(opts: ScrollOptions): Promise<void>`

Scrolls at a given position. Positive `dy` scrolls down, negative scrolls up.

```typescript
// Scroll down 300px from the center of the screen
await client.scroll({ x: 200, y: 400, dy: 300 });

// Scroll up
await client.scroll({ x: 200, y: 400, dy: -300 });

// Horizontal scroll
await client.scroll({ x: 200, y: 400, dx: 200, dy: 0 });
```

#### `client.longPress(target: TapTarget | FinderTarget): Promise<void>`

Long-presses a widget. Same targeting options as `tap()`.

```typescript
await client.longPress({ findBy: 'text', value: 'Delete' });
```

#### `client.findWidget(by: string, value: string): Promise<Record<string, unknown> | null>`

Finds a widget and returns its bounding box, or `null` if not found. Finder types: `text`, `key`, `type`, `semantics`.

```typescript
const widget = await client.findWidget('text', 'Increment');
if (widget) {
  console.log(`Found at (${widget.centerX}, ${widget.centerY})`);
}
```

---

### Development

#### `client.hotReload(): Promise<void>`

Triggers a hot reload of the Flutter app. Applies code changes while preserving app state.

#### `client.hotRestart(): Promise<void>`

Triggers a hot restart of the Flutter app. Reloads all code and resets app state. Waits 2 seconds for the app to stabilize before returning.

#### `client.evaluate(expression: string): Promise<EvalResult>`

Evaluates a Dart expression in the context of the running app and returns the result.

```typescript
const result = await client.evaluate('DateTime.now().toString()');
console.log(result.value); // "2025-01-15 10:30:00.000"
```

---

## Connection

### VM Service URI

When you run `flutter run`, the Dart VM service URI is printed in the console:

```
A Dart VM Service on Chrome is available at: ws://127.0.0.1:56789/ABCDEF=/ws
```

`FlutterPilotClient.connect()` accepts this URI directly. It also handles automatic conversion from `http://` to `ws://` and appends `/ws` if missing.

### Auto-Reconnect

The underlying `DartVMConnection` automatically attempts to reconnect if the WebSocket connection drops (up to 5 attempts with exponential backoff starting at 1 second). Auto-reconnect is disabled when you call `disconnect()`.

---

## Types

The package exports all relevant TypeScript interfaces:

```typescript
import type {
  WidgetNode,
  WidgetRect,
  SemanticsNode,
  InteractiveWidget,
  WidgetDetails,
  EvalResult,
  FinderType,      // 'text' | 'key' | 'type' | 'semantics'
  TapTarget,       // { x: number; y: number }
  FinderTarget,    // { findBy: FinderType; value: string }
  ScrollOptions,   // { x: number; y: number; dx?: number; dy: number }
  FlutterView,
  IsolateRef,
  VMInfo,
} from '@flutterpilot/core';
```

## Requirements

| Dependency | Minimum Version |
|---|---|
| Node.js | >= 18 |
| Flutter app | Running in debug mode |
| flutter_pilot (Dart) | Installed in the target app (required for interaction and semantics) |

## License

MIT