# 06 · Seu próprio plugin

Ligar plugins do catálogo é ótimo, mas em algum momento você quer uma capacidade que só existe no seu produto. O incubator é o caminho: um plugin app-local que segue o **mesmo contrato** dos plugins oficiais. É o passo que transforma você de usuário do SDK em autor.

## Gere o plugin

```bash
npx @fayz-ai/cli create plugin fidelidade
```

```
✓ Created app-local plugin "src/plugins/fidelidade"

  Add createFidelidadePlugin() to your app's plugins array (see src/plugins/fidelidade/README.md).
  Run "fayz doctor" to check boundaries.
```

O que foi criado em `src/plugins/fidelidade/`:

| Arquivo | Papel |
| --- | --- |
| `index.ts` | O `PluginManifest`: id, navegação, rotas e a página. O mesmo contrato de um `@fayz-ai/plugin-*`. |
| `data/types.ts` | O contrato de dados (`FidelidadeDataProvider`) que todo backend implementa. |
| `data/mock.ts` | Provider mock — o plugin já roda sem banco. |
| `data/supabase.ts` | Provider real, passando pelo boundary do Fayz (`getSupabaseClientOptional`), nunca importando o SDK do Supabase direto. |
| `schema/index.ts` | Onde as migrations do plugin vivem. |
| `README.md` | Como fiar o plugin e a checklist de graduação para oficial. |

## O contrato é o mesmo dos oficiais

O `index.ts` exporta uma factory que devolve um `PluginManifest` — id, `navigation`, `routes` e uma página. Ele escolhe o provider automaticamente (Supabase quando há backend, mock caso contrário) via `createSafeDataProvider`:

```ts
export function createFidelidadePlugin(options?: FidelidadePluginOptions): PluginManifest {
  const provider =
    options?.dataProvider ??
    createSafeDataProvider(
      () => createSupabaseFidelidadeProvider(),
      () => createMockFidelidadeProvider(),
    )
  // ...
  return {
    id: 'fidelidade',
    name: 'Fidelidade',
    navigation: [{ section: 'main', label: 'Fidelidade', route: '/fidelidade', icon: 'Puzzle' }],
    routes: [{ path: '/fidelidade', component: Page }],
  }
}
```

Para ligá-lo, adicione `createFidelidadePlugin()` ao array de plugins da config do app (veja o `README.md` do plugin). E confira os limites com o doctor:

```bash
npx @fayz-ai/cli doctor
```

{% callout type="tip" %}
✓ Você deve ver: a pasta `src/plugins/fidelidade/` com os seis arquivos, e o `doctor` com `0 error(s)`. O `data/supabase.ts` já usa `getSupabaseClientOptional` — se você importasse `@supabase/supabase-js` direto, o doctor acusaria a quebra de boundary.
{% /callout %}

## Onde vão as migrations

As tabelas do seu plugin vivem em `schema/index.ts` e são referenciadas no manifesto via `migrations: [...]`:

```ts
migrations: [{ id: 'fidelidade-0001', version: '0.1.0', sql: '<create table ...>' }]
```

Quando você rodar `fayz db apply`, o passo `incubator` (o último da ordem) aplica essas migrations junto com o resto — foi por isso que a ordem `spine → drizzle → seed → plugins → incubator` importa.

## De incubator a oficial

Promover um plugin app-local para um pacote `@fayz-ai/plugin-*` oficial é **empacotamento, não reescrita**: o manifesto não muda, você só move a pasta. O `README.md` gerado traz a checklist completa de graduação (contrato validado, teste de capacidade, permissões, i18n, boundary limpo). O mesmo contrato que você seguiu aqui é o que os plugins do catálogo seguem.

---

[← Anterior: 05 · Dados reais com Supabase](/pt-BR/docs/tutorial/05-dados-reais-com-supabase) · [Próximo: 07 · Publicar →](/pt-BR/docs/tutorial/07-publicar)
