DeepSeek Harness: Ciclo de Vida do Plugin

Última atualização: 2026-08-31

Cada plugin no Cordis não é um bloco de código estático, mas uma entidade viva com um ciclo de vida — um Fiber. Entender a Fiber state machine significa entender a jornada completa de "aguardando para carregar" a "executando" a "saída graciosa," que é a chave para escrever plugins robustos.

💡 Dica: O valor central do Fiber é "ciclo de vida observável" — cada transição de estado tem condições de gatilho claras e efeitos colaterais observáveis. Você sempre sabe em que estado um plugin está, e por quê.

📋 Pré-requisitos: Ter completado 13-effect.md, entender auto-limpeza e ctx.effect()

1. O Que Você Vai Aprender

Máquina de Estados Fiber


2. Conceito Fiber

(1) O Que É Fiber

▶ Exemplo 1: Interface Fiber

Fiber é a abstração do Cordis sobre o estado de runtime de um plugin — cada instância de plugin corresponde a um objeto Fiber que registra em qual estágio do ciclo de vida o plugin está atualmente.

TYPESCRIPT
interface Fiber {
  id: string
  name: string
  state: FiberState
  context: Context
  parent: Fiber | null
  children: Fiber[]
}

(2) Por Que Fiber É Necessário

Sem Fiber, plugins têm apenas dois estados: "carregado" e "descarregado." Mas na prática:

Fiber fornece um modelo claro de state machine para esses estados intermediários.

(3) Fiber e Context

TEXT 📖 Somente leitura
Fiber = "alma" do plugin (informação de estado)
Context = "corpo" do plugin (recursos e ambiente)

Cada Fiber possui um Context; o ciclo de vida do Context está vinculado ao Fiber.


3. Estados do Ciclo de Vida

(1) Diagrama de Estados

100%
stateDiagram-v2
    [*] --> pending: Plugin registrado
    pending --> active: Dependências prontas + apply bem-sucedido
    pending --> errored: apply falha
    active --> disposing: Descarregamento solicitado
    errored --> active: Tentativa bem-sucedida
    errored --> disposing: Tentativa abandonada
    disposing --> disposed: Limpeza concluída

(2) Significados dos Estados

Estado Significado ctx Disponível Reversível
pending Aguardando dependências Limitado
active Executando normalmente Completo
disposing Limpando recursos Somente leitura
disposed Destruído Indisponível
errored Falha na inicialização Indisponível

(3) Estado pending

Quando um plugin declara inject mas as dependências ainda não foram registradas, Fiber entra em pending:

TYPESCRIPT
export const inject = ['tools']

// Serviço tools ainda não registrado → Fiber está pending
// tools registrado → Fiber transiciona para active, chama apply

Durante pending:

(4) Estado active

Após apply executar com sucesso, Fiber entra em active:

TYPESCRIPT
export function apply(ctx: Context) {
  // Fiber agora está active
  ctx.logger.info('Estou vivo!')
  
  ctx.on('session/created', (s) => {
    // Pode usar todos os serviços normalmente
  })
}

Durante active:

(5) Estado disposing

Após receber uma solicitação de descarregamento, Fiber entra em disposing e começa a limpar recursos:

TEXT 📖 Somente leitura
Processo disposing:
1. Parar de aceitar novas requisições
2. Executar funções de limpeza ctx.effect() em ordem inversa
3. Auto-remover listeners de eventos
4. Limpar timers
5. Desregistrar comandos e serviços

(6) Estado disposed

Após todos os recursos serem limpos, Fiber entra em disposed:


4. Criação e Destruição de Fiber

(1) Momento de Criação

Fiber é automaticamente criado quando um plugin é registrado:

TYPESCRIPT
// Lógica interna do framework (pseudocódigo)
function registerPlugin(plugin: PluginDefinition) {
  const fiber = new Fiber({
    id: generateId(),
    name: plugin.name,
    state: hasDeps(plugin) ? 'pending' : 'active'
  })
  
  if (fiber.state === 'active') {
    fiber.context = createContext(fiber)
    plugin.apply(fiber.context)
  }
}

(2) Gatilhos de Destruição

Destruição de Fiber é acionada por:

Gatilho Descrição
ctx.dispose() Descarregamento ativo
Fiber pai destruído Descarregamento em cascata
Recarregamento por mudança de config Fiber antigo destruído, novo Fiber criado

(3) Ordem de Destruição

100%
graph TD
    TRIGGER[Gatilho de descarregamento] --> STOP[Parar novas requisições]
    STOP --> CHILD[Destruir Fibers filhos]
    CHILD --> CLEAN[Executar funções de limpeza]
    CLEAN --> DISPOSED[Marcar disposed]

5. Relações Fiber Pai-Filho

(1) Estrutura Hierárquica

Fibers suportam relações pai-filho, formando estruturas em árvore:

100%
graph TD
    ROOT[Root Fiber<br/>dsh-core] --> A[Plugin A Fiber]
    ROOT --> B[Plugin B Fiber]
    A --> A1[Sub-plugin A1]
    A --> A2[Sub-plugin A2]

(2) Estabelecendo Relações Pai-Filho

▶ Exemplo 2: Criando Sub-Plugins via ctx.plugin()

Crie Fibers filhos via ctx.plugin():

TYPESCRIPT
export function apply(ctx: Context) {
  ctx.plugin({
    name: 'sub-plugin',
    apply(subCtx: Context) {
      subCtx.logger.info('Eu sou um fiber filho')
    }
  })
}

(3) Destruição em Cascata

Quando um Fiber pai é destruído, todos os Fibers filhos são automaticamente destruídos:

TEXT 📖 Somente leitura
Descarregar Plugin A:
  → Primeiro destruir Sub-plugin A1
  → Depois destruir Sub-plugin A2
  → Finalmente destruir Plugin A

Esta ordem de destruição "filhos antes do pai" garante que dependências não sejam quebradas.

(4) Herança de Escopo

Fibers filhos herdam o ctx do Fiber pai:

TYPESCRIPT
// Serviços registrados pelo plugin pai são acessíveis aos plugins filhos
export function apply(ctx: Context) {
  ctx.provide('parent-service', { ... })
  
  ctx.plugin({
    name: 'child',
    inject: ['parent-service'],
    apply(childCtx) {
      childCtx['parent-service'] // ✅ Pode acessar serviço do pai
    }
  })
}

6. Tratamento de Erros e Rollback de Estado

(1) Falha no apply

Quando apply lança uma exceção, Fiber entra no estado errored:

TYPESCRIPT
export function apply(ctx: Context) {
  throw new Error('inicialização falhou')
  // Fiber → errored
}

(2) Tentativa Automática

Cordis automaticamente tenta novamente Fibers com erro:

TEXT 📖 Somente leitura
1ª tentativa: apply() → throw Error → errored
2ª tentativa: (aguardar 1s) apply() → throw Error → errored
3ª tentativa: (aguardar 2s) apply() → throw Error → errored
4ª tentativa: (aguardar 4s) apply() → sucesso → active

O intervalo de tentativa usa backoff exponencial: 1s → 2s → 4s → 8s → ... → máx 60s.

(3) Configuração da Estratégia de Tentativa

TYPESCRIPT
export const Config = Schema.object({
  maxRetries: Schema.number().default(5).description('Máximo de tentativas'),
  retryInterval: Schema.number().default(1000).description('Intervalo inicial de tentativa (ms)')
})

(4) Tentativa Manual

TYPESCRIPT
ctx.on('fiber/errored', (fiber) => {
  ctx.logger.warn(`plugin ${fiber.name} com erro, tentando novamente...`)
  fiber.retry()
})

(5) Erros Não Retentáveis

Alguns erros não devem ser tentados novamente:

TYPESCRIPT
export function apply(ctx: Context) {
  if (!process.env.REQUIRED_VAR) {
    // Erro de configuração, tentar novamente não ajuda
    throw new NonRetryableError('REQUIRED_VAR não está definida')
  }
}

(6) Erros na Fase de Limpeza

Erros durante a fase disposing não impedem Fiber de transicionar para disposed, mas são registrados:

TEXT 📖 Somente leitura
[warn] erro de limpeza no plugin my-plugin: Conexão já fechada
[info] plugin my-plugin disposed (com 1 avisos de limpeza)

7. Isolamento de Contexto Fiber

(1) Cada Fiber Tem um ctx Independente

TYPESCRIPT
const fiberA = new Fiber({ name: 'plugin-a' })
const fiberB = new Fiber({ name: 'plugin-b' })

fiberA.context !== fiberB.context  // true

(2) Limites de Isolamento

Recurso Isolado Descrição
Listeners de evento Cada Fiber registra independentemente
Timers Cada Fiber limpa independentemente
Comandos ⚠️ Compartilhados globalmente, mas com escopos
Serviços Compartilhados globalmente
Configuração Cada plugin é independente

(3) Equilibrando Compartilhamento e Isolamento de Serviços

Serviços são compartilhados globalmente — esta é uma decisão de design do Cordis. O serviço A registrado pelo plugin A pode ser usado pelo plugin B via inject. Se isolamento é necessário, use o mecanismo de escopo (veja 20-scope.md).

(4) Consultas de Estado

▶ Exemplo 3: Consultando Estado do Fiber

TYPESCRIPT
// Consultar estado do Fiber
ctx.fiber.state           // 'active'
ctx.fiber.id              // 'fiber-abc-123'
ctx.fiber.parent          // Fiber pai ou null
ctx.fiber.children        // Fiber[]

❓ Perguntas Frequentes

P Qual a relação entre Fiber e Thread?
R Nenhuma. Fiber não é uma thread do SO, mas uma unidade de estado lógico do Cordis. Todos os Fibers executam no mesmo event loop do Node.js.
P Posso criar um Fiber manualmente?
R Não recomendado. Ao registrar sub-plugins via ctx.plugin(), o framework automaticamente cria Fibers.
P Plugins com erro consomem recursos?
R Não. Um Fiber com erro não mantém recursos ctx; apenas o objeto Fiber em si (memória mínima) é retido para rastreamento de estado.
P Um Fiber filho pode sobreviver após seu pai ser destruído?
R Não. Relações pai-filho são dependências fortes — destruição do pai sempre destrói os filhos. Se precisar de ciclos de vida independentes, não estabeleça relações pai-filho.
P Como monitorar todos os estados de Fiber?
R Escute eventos de ciclo de vida do Fiber: typescript ctx.on('fiber/created', (fiber) => { ... }) ctx.on('fiber/active', (fiber) => { ... }) ctx.on('fiber/disposing', (fiber) => { ... }) ctx.on('fiber/disposed', (fiber) => { ... }) ctx.on('fiber/errored', (fiber) => { ... })
P Quando mudanças de config causam reconstrução do Fiber, as funções de limpeza antigas são executadas?
R Sim. Na mudança de config, o Fiber antigo passa pelo fluxo completo disposing → disposed; todas as funções de limpeza são executadas. Depois um novo Fiber é criado e entra em pending/active.

📖 Resumo


📝 Exercícios

1. ⭐ Básico: Escreva um plugin que exiba o estado e id do Fiber atual em apply. Inicie e verifique os logs para confirmar que o Fiber está no estado active.

2. ⭐⭐ Intermediário: Escreva um plugin que deliberadamente lança um erro em apply (simulando falha de inicialização). Observe o estado errored do Fiber e o comportamento de tentativa. Depois corrija o erro e verifique que o Fiber recupera para active.

3. ⭐⭐⭐ Desafio: Crie uma estrutura Fiber pai-filho: o plugin pai registra um serviço, o plugin filho o usa via inject. Descarregue o plugin pai e verifique que o filho também é destruído em cascata. Exiba a ordem de destruição nas funções de limpeza para confirmar "filhos antes do pai."

Web-Tutorial.com

Equipe Técnica Web-Tutorial

Uma plataforma de tutoriais mantida por diversos desenvolvedores. Cada tutorial é escrito e revisado por profissionais da área correspondente. Trabalhamos para manter nosso conteúdo preciso e confiável — se encontrar algum problema, avise-nos.

100%