npm package: @stattus4/native-engine
Description: Stattus4 engine com arquitetura Clean/Hexagonal e facade única para integração com SuperApp.
Author: STT4jeferson
Latest version: 0.1.1

Corrigindo workflow

# @stattus4/native-engine

Biblioteca nativa da Stattus4 para integração com o SuperAPP via uma única fachada (`Engine`), com suporte a gateway (TCP/BLE), pipeline de áudio, observabilidade e integração opcional com bridges nativas (TurboModule).

Este documento é a documentação funcional da biblioteca. Ele descreve a API pública e o comportamento atual do pacote `@stattus4/native-engine`.

## Visão geral

A `native-engine` centraliza:

- ciclo de vida da engine (`start`, `stop`, `destroy`)
- conexão de gateway (`connect`, `disconnect`, `sendGatewayCommand`)
- captura/reprodução de áudio e exportação de gravação
- eventos de engine/gateway/áudio/protocolo
- diagnósticos e métricas
- fallback em memória quando bridges nativas não estão disponíveis

## Suporte de plataforma (estado atual)

- `Engine` (núcleo TypeScript): multiplataforma
- factories TurboModule (`createTurboNativeGatewayBridge`, `createTurboNativeAudioBridge`): Android (retornam `undefined` fora de Android)
- bridges customizadas (`config.native`): suportadas via interfaces públicas (`NativeGatewayBridge`, `NativeAudioBridge`)

Observação: o pacote contém código nativo iOS/Android, mas o auto-discovery de bridge TurboModule exportado hoje está habilitado apenas para Android.

## Requisitos (recomendado)

- Node.js `>= 20` (validado no app `example`)
- React Native (exemplo usa `0.83.0`)
- Android `minSdkVersion 24` (conforme `android/build.gradle`)

## Instalação no repositório

Este pacote está dentro do repositório `stattus4-SuperAPP-lib` em `@stattus4/native-engine/`.

```bash
cd @stattus4/native-engine
npm install
```

Para rodar o app de exemplo:

```bash
npm run example:android
# ou
npm run example:ios
```

## Publicação pública no npm

O pacote está preparado para publicação pública no npm sob o escopo `@stattus4`.

Pré-requisitos:

- conta npm com permissão para publicar no escopo `@stattus4`
- autenticação no registry `https://registry.npmjs.org/`

Publicação:

```bash
cd @stattus4/native-engine
npm login
npm publish
```

O `package.json` define `publishConfig.access = "public"` e `publishConfig.registry = "https://registry.npmjs.org/"`, então o primeiro publish do pacote escopado já sai como público no npm.

### Publicação via GitHub Actions

O repositório agora possui o workflow `.github/workflows/publish-native-engine.yml`, que:

- instala as dependências com `npm ci`
- valida com `npm run typecheck` e `npm run test:unit`
- publica `@stattus4/native-engine` no npm com o secret `NPM_TOKEN`

Antes de usar o workflow, configure em `Settings > Secrets and variables > Actions` um secret `NPM_TOKEN` com um token do npm que tenha permissão para publicar no escopo `@stattus4`.

Você pode disparar esse publish de duas formas:

- manualmente em `Actions > Publish @stattus4/native-engine to npm`
- automaticamente ao subir uma tag no formato `native-engine-v*`

Fluxo sugerido para release:

```bash
cd @stattus4/native-engine
npm version patch
git add package.json package-lock.json
git commit -m "chore: release 0.1.1"
git tag native-engine-v0.1.1
git push origin main --follow-tags
```

Instalação em outro projeto:

```bash
npm install @stattus4/native-engine
```

Depois disso, basta declarar a dependência normalmente no `package.json` do app:

```json
{
  "dependencies": {
    "@stattus4/native-engine": "0.1.0"
  }
}
```

## Início rápido

```ts
import {
  createEngine,
  createTurboNativeAudioBridge,
  createTurboNativeGatewayBridge,
  type Engine,
} from '@stattus4/native-engine';

const nativeGateway = createTurboNativeGatewayBridge();
const nativeAudio = createTurboNativeAudioBridge();

const engine: Engine = createEngine({
  appId: 'superapp',
  gateway: {
    mode: 'auto',
    port: 3000,
    reconnectOnFailure: true,
  },
  audio: {
    sampleRate: 16_000,
    channels: 1,
    bitsPerSample: 16,
    chunkSizeBytes: 320,
  },
  security: {
    enableEncryption: false,
    enableSigning: false,
  },
  protocol: {
    daqExpectedBytes: 160_000,
    streamIdleTimeoutMs: 4_000,
    getPermission: () => true,
    getProtocolVersion: () => 3,
    getAvailableFirmware: () => false,
  },
  retry: {
    maxAttempts: 3,
    baseDelayMs: 300,
    maxDelayMs: 2_000,
    jitter: 0.2,
  },
  timeout: {
    connectMs: 5_000,
    commandMs: 4_000,
    captureStartMs: 2_000,
  },
  observability: {
    level: 'info',
    enableMetrics: true,
    enableDiagnostics: true,
  },
  native: {
    ...(nativeGateway ? { gateway: nativeGateway } : {}),
    ...(nativeAudio ? { audio: nativeAudio } : {}),
  },
  // Em Android produção, você pode habilitar modo estrito:
  // capabilities: ANDROID_SUPERAPP_PRODUCTION_CAPABILITIES,
});

const unsubscribe = engine.onEvent((event) => {
  if (event.type === 'engine_error') {
    console.warn(event.error.code, event.error.message);
  }
});

(async () => {
  const started = await engine.start();
  if (!started.ok) return;

  const connected = await engine.connect();
  if (!connected.ok) return;

  const encoder = new TextEncoder();
  await engine.sendGatewayCommand({
    payload: encoder.encode('AT+INFO\r\n'),
  });

  const snapshot = engine.snapshot();
  console.log(snapshot.gatewayState, snapshot.audioState);

  const diagnostics = await engine.diagnostics();
  if (diagnostics.ok) {
    console.log(diagnostics.value);
  }

  await engine.disconnect();
  await engine.stop();
  unsubscribe();
  engine.destroy();
})();
```

## Conceitos principais

### 1. `Engine` (fachada única)

A interface `Engine` concentra o uso público da biblioteca:

- lifecycle: `start`, `stop`, `destroy`
- gateway: `connect`, `disconnect`, `sendGatewayCommand`
- áudio: `startCapture`, `stopCapture`, `playAudioFile`, exportação de gravações
- observabilidade: `onEvent`, `snapshot`, `diagnostics`

### 2. Resultado padronizado (`Result<T, EngineError>`)

A API assíncrona retorna `Result` em vez de lançar exceções na maioria das operações:

- sucesso: `{ ok: true, value }`
- erro: `{ ok: false, error }`

Exceções ainda podem acontecer em `createEngine(...)` quando há configuração inválida ou exigência de capability não atendida (ex.: `strictNative` sem bridge nativa disponível).

### 3. Fallback automático

Se as bridges nativas não estiverem disponíveis e você não exigir modo estrito, a engine usa adapters em memória para gateway/áudio. Isso é útil para:

- desenvolvimento local
- testes unitários
- execução em ambientes sem TurboModule

## API pública (resumo)

### Criação e exports principais

- `createEngine(config: EngineConfig): Engine`
- `createTurboNativeGatewayBridge(): NativeGatewayBridge | undefined`
- `createTurboNativeAudioBridge(): NativeAudioBridge | undefined`
- `ANDROID_SUPERAPP_PRODUCTION_CAPABILITIES`
- `EngineError`, `EngineErrorCode`, `engineError(...)`
- `ok(...)`, `err(...)`

### Métodos da `Engine`

- `start()`
  - Inicializa a engine e publica `engine_started`.
- `stop()`
  - Para captura de áudio, desconecta gateway e publica `engine_stopped`.
- `connect()`
  - Conecta o gateway configurado (`tcp`/`ble`). Requer `start()` antes.
- `disconnect()`
  - Desconecta o gateway atual.
- `sendGatewayCommand({ payload })`
  - Envia bytes para o gateway.
- `startCapture({ mode? })`
  - Inicia pipeline de áudio manualmente (`play` ou `play_and_record`).
- `stopCapture()`
  - Para a captura/pipeline de áudio.
- `playAudioFile(wavAudio)`
  - Reproduz um arquivo WAV via bridge de áudio nativa.
- `saveLastRecordingWav({ sink? })`
  - Exporta a última gravação para WAV (e opcionalmente grava em `sink.write(...)`).
- `getLastRecordingPcm()`
  - Retorna o PCM bruto da última gravação.
- `clearLastRecording()`
  - Limpa buffers de gravação exportável.
- `snapshot()`
  - Snapshot síncrono do estado de execução/gateway/áudio.
- `diagnostics()`
  - Diagnóstico completo (capacidades, métricas, protocolo, gateway, áudio).
- `onEvent(listener)`
  - Assina eventos da engine.
- `destroy()`
  - Libera listeners/recursos internos. Chame no teardown da tela/processo.

## Configuração (`EngineConfig`)

### Campos obrigatórios

```ts
interface EngineConfig {
  appId: string;
  gateway: GatewayConfig;
  audio: AudioConfig;
  security: SecurityConfig;
  retry: RetryPolicy;
  timeout: TimeoutPolicy;
  observability: ObservabilityConfig;
  productScope?: EngineProductScopeConfig;
  protocol?: ProtocolConfig;
  capabilities?: EngineCapabilitiesConfig;
  native?: EngineNativeBridges;
}
```

Observação: `createEngine()` aplica defaults internos para `productScope`, `protocol`, `retry` e `timeout`, mas os tipos públicos exigem `retry` e `timeout` explicitamente.

### `gateway` (`GatewayConfig`)

```ts
{
  mode: 'tcp' | 'ble' | 'auto';
  host?: string;
  port?: number;
  deviceName?: string;
  scanTimeoutMs?: number;
  reconnectDelayMs?: number;
  updateRateMs?: number;
  reconnectOnFailure: boolean;
  initialState?: GatewayState;
}
```

Regras de resolução quando `mode: 'auto'`:

- se `host` ou `port` estiver definido: usa `tcp`
- senão, se `deviceName` estiver definido: tenta `ble` quando houver bridge nativa de gateway; caso contrário, cai para `tcp`
- caso contrário: usa `tcp`

### `audio` (`AudioConfig`)

```ts
{
  sampleRate: number;
  channels: number;
  bitsPerSample: number;
  chunkSizeBytes: number;
}
```

Configura o pipeline de áudio (captura/reprodução) e também influencia defaults de gravação/DAQ.

### `security` (`SecurityConfig`)

```ts
{
  enableEncryption: boolean;
  enableSigning: boolean;
}
```

Esses flags fazem parte do contrato público da engine e da configuração de produto. No estado atual da API pública, eles são principalmente declarativos (a fachada `Engine` não expõe operações de criptografia/assinatura diretamente).

### `protocol` (`ProtocolConfig`)

A engine processa comandos/eventos de protocolo sobre payloads de gateway (especialmente no fluxo TCP/AT). Campos relevantes:

- `daqExpectedBytes`
- `trackExpectedBytes`
- `streamIdleTimeoutMs`
- `requestsEnabled` / `getPermission`
- `protocolVersion` / `getProtocolVersion`
- `firmwareAvailable` / `getAvailableFirmware`

Comandos AT reconhecidos no runtime atual (via parser interno):

- `AT+START`
- `AT+DAQ`
- `AT+DAQPOS`
- `AT+STREAM`
- `AT+INFO`
- `AT+UPDATE`
- `AT+DEVICE`
- `AT+ALARM`
- `AT+HBEAT`
- `AT+UTRACK`

### `observability` (`ObservabilityConfig`)

```ts
{
  level: 'debug' | 'info' | 'warn' | 'error' | 'silent';
  enableMetrics: boolean;
  enableDiagnostics: boolean;
}
```

- `enableMetrics`: habilita coleta de métricas e eventos `metric_observed`
- `enableDiagnostics`: habilita `engine.diagnostics()`

### `productScope` (`EngineProductScopeConfig`)

Metadata de produto exposta em `diagnostics()`:

- `fota`: `'deferred' | 'partial' | 'ready'`
- `gps`: `'deferred' | 'partial' | 'ready'`
- `security`: `'deferred' | 'partial' | 'ready'`

Default:

```ts
{
  fota: 'deferred',
  gps: 'deferred',
  security: 'deferred',
}
```

### `capabilities` (`EngineCapabilitiesConfig`)

Controla fallback/uso de bridges nativas:

- `forceFallback`
- `allowNativeGateway`
- `allowNativeAudio`
- `allowSecureStore`
- `requireNativeGateway`
- `requireNativeAudio`
- `strictNative` (atalho para exigir gateway + áudio nativos)

Preset exportado para Android produção:

```ts
ANDROID_SUPERAPP_PRODUCTION_CAPABILITIES
// => { forceFallback: false, allowNativeGateway: true, allowNativeAudio: true, strictNative: true }
```

## Bridges nativas (`config.native`)

Você pode injetar bridges customizadas para adaptar a engine ao host/plataforma:

- `native.gateway?: NativeGatewayBridge`
- `native.audio?: NativeAudioBridge`

### Interface `NativeGatewayBridge` (resumo)

Métodos principais:

- `configure?(config)`
- `connect()`
- `disconnect()`
- `send(payload)`
- `onData(listener)`
- `onDisconnect(listener)`

Métodos opcionais para richer TCP lifecycle:

- `closeClient?()`
- `onClientConnected?(listener)`
- `onClientDisconnected?(listener)`

### Interface `NativeAudioBridge` (resumo)

- `startCapture(config)`
- `stopCapture()`
- `playFrame?(frame, config)`
- `playWav?(payload)`
- `getPlaybackHeadPosition?()`

### Factories TurboModule (Android)

- `createTurboNativeGatewayBridge()`
- `createTurboNativeAudioBridge()`

As factories tentam localizar o TurboModule `NativeEngine` e retornam `undefined` quando não disponível (ou fora de Android).

## Gateway: modos `tcp` e `ble`

### TCP

- A engine atua com um servidor TCP (via bridge nativa) e recebe dados do cliente (sensor/dispositivo).
- Eventos de cliente (`gateway_client_connected`, `gateway_client_disconnected`) e estado de servidor (`gateway_transport_state_changed`) são emitidos quando suportados pela bridge.
- Erro de bind de porta (`EADDRINUSE`) é normalizado para `EngineErrorCode.GATEWAY_PORT_IN_USE`.

### BLE

- O modo BLE usa `gateway.deviceName` e os parâmetros de scan/reconnect/update.
- O comportamento exato depende da `NativeGatewayBridge` fornecida.
- Na bridge TurboModule Android atual, comandos enviados pela engine são mapeados para solicitações de bateria/GPS no runtime BLE.

## Áudio e gravação

### Captura manual

```ts
await engine.startCapture({ mode: 'play_and_record' });
await engine.stopCapture();
```

Modos:

- `play`
- `play_and_record` (default da captura)

### Pipeline automático por protocolo

A engine também controla o áudio automaticamente em ciclos de protocolo:

- estado de protocolo `daq` -> inicia pipeline em `play_and_record`
- estado de protocolo `stream` -> inicia pipeline em `play`
- estado `idle`/`error` -> desliga pipeline

### Exportação da última gravação

- `saveLastRecordingWav()` retorna WAV (`Uint8Array`)
- `getLastRecordingPcm()` retorna PCM bruto (`Uint8Array`)
- `clearLastRecording()` limpa buffers exportáveis

Se não houver gravação disponível, a engine retorna `AUDIO_RECORDING_UNAVAILABLE`.

Você também pode persistir a exportação via `sink`:

```ts
await engine.saveLastRecordingWav({
  sink: {
    write(payload) {
      // salvar em arquivo / upload / persistência
    },
  },
});
```

## Eventos (`engine.onEvent(...)`)

A engine publica eventos tipados (`EngineEvent`). Principais grupos:

### Lifecycle da engine

- `engine_started`
- `engine_stopped`
- `engine_error`

### Gateway

- `gateway_state_changed`
- `gateway_transport_state_changed`
- `gateway_client_connected`
- `gateway_client_disconnected`
- `gateway_data_received`
- `gateway_data_sent`

### Áudio

- `audio_state_changed`
- `audio_frame_captured`

### Observabilidade

- `metric_observed`

### Protocolo

- `fluidcore_protocol_event`

Observação: o nome do evento/tipo público (`fluidcore_protocol_event`, `FluidCoreProtocolEvent`) é mantido por compatibilidade da API atual. Ele representa os eventos de protocolo processados pela engine.

Campos úteis em `fluidcore_protocol_event`:

- `message`
- `state`
- `command?`
- `payload?`
- `device?`
- `clientId?`
- `sessionId?`
- `progress?`

## Snapshot e diagnósticos

### `snapshot()`

Leitura síncrona e leve do estado atual:

- `running`
- `gatewayState`
- `audioState`
- `connectedAt`
- `gateway` (client count, active client, timestamps de atividade, estado do servidor)

### `diagnostics()`

Retorna um snapshot estendido (`EngineDiagnostics`) com:

- `capabilities` (fallback, nativeGateway, nativeAudio, secureStore)
- `productScope`
- `gateway` (bytes, reconnectAttempts, lastError, cliente ativo)
- `protocol` (estado, último comando, último payload de device, metadados do device)
- `audio` (frames, dropped, bytes)
- `metrics`

Se `observability.enableDiagnostics === false`, retorna erro com `EngineErrorCode.CAPABILITY_UNAVAILABLE`.

## Códigos de erro públicos (`EngineErrorCode`)

Lista atual:

- `INVALID_ARGUMENT`
- `NOT_READY`
- `ALREADY_RUNNING`
- `TIMEOUT`
- `PERMISSION_DENIED`
- `CAPABILITY_UNAVAILABLE`
- `GATEWAY_PORT_IN_USE`
- `GATEWAY_CONNECT_FAILED`
- `GATEWAY_DISCONNECTED`
- `GATEWAY_SEND_FAILED`
- `AUDIO_START_FAILED`
- `AUDIO_STOP_FAILED`
- `AUDIO_PLAYBACK_FAILED`
- `AUDIO_RECORDING_UNAVAILABLE`
- `AUDIO_EXPORT_FAILED`
- `STORAGE_FAILED`
- `SECURITY_FAILED`
- `INTERNAL_ERROR`

## Boas práticas de uso

- Chame `start()` antes de `connect()`.
- Sempre faça teardown com `disconnect()`/`stop()` e `destroy()`.
- Use `strictNative` em produção Android quando a bridge nativa for obrigatória.
- Em desenvolvimento/testes, permita fallback para acelerar validação.
- Assine `engine_error` via `onEvent` para telemetria e troubleshooting.

## Troubleshooting

### `connect()` falha com `GATEWAY_PORT_IN_USE`

A porta TCP já está em uso. Troque `gateway.port` ou encerre o processo que está ocupando a porta.

### `createEngine(...)` lança erro de capability nativa

Exemplo comum:

- `Native gateway bridge is required but unavailable`

Isso ocorre quando `strictNative` / `requireNativeGateway` / `requireNativeAudio` está ativo e a bridge correspondente não foi encontrada/injetada.

### `connect()` retorna `NOT_READY`

`engine.start()` não foi chamado antes de `engine.connect()`.

### `diagnostics()` indisponível

Verifique `observability.enableDiagnostics`. Quando `false`, a engine retorna `CAPABILITY_UNAVAILABLE`.

## Desenvolvimento da biblioteca

Dentro de `@stattus4/native-engine/`:

```bash
npm install
npm run typecheck
npm run lint
npm run test:unit
```

Testes Android (dependem de ambiente Android configurado):

```bash
npm run test:android:native
npm run test:android:e2e
```

## Exemplo de integração

O app de exemplo em `example/` demonstra:

- criação da engine
- uso das factories TurboModule
- fluxo de ações (`start`, `connect`, envio de comando, captura, diagnósticos)
- renderização de `snapshot()` e stream de eventos

Arquivo principal: `example/src/App.tsx`.

## Licença

MIT. Veja `LICENSE`.