> ## Documentation Index
> Fetch the complete documentation index at: https://docs.lerian.studio/llms.txt
> Use this file to discover all available pages before exploring further.

# Embeber el Fetcher Engine

> Importa el módulo Go del Fetcher Engine, provee los puertos que necesita y constrúyelo con engine.New — con un ejemplo autocontenido que corre sin ninguna infraestructura.

Embeber el [Fetcher Engine](/es/fetcher/fetcher-engine-overview) toma tres pasos: **impórtalo, provee los puertos que necesita, constrúyelo con `engine.New`.** La importación no trae infraestructura. Conectas solo las partes que tu aplicación anfitriona usa de verdad.

## 1. Instala

***

```bash theme={null}
go get github.com/LerianStudio/fetcher/pkg/engine
```

<Note>
  El Engine es un módulo Go distinto de los servicios de Fetcher (`github.com/LerianStudio/fetcher/v2`). Lleva cero dependencias de terceros y tiene su propia línea de versiones, con tags prefijados por ruta (`pkg/engine/vX.Y.Z`). La importación no aporta al grafo de tu módulo ninguna dependencia de los servicios.
</Note>

## 2. Provee los puertos

***

El Engine depende solo de interfaces que provee el host. Un puerto es siempre obligatorio. Un segundo lo es solo cuando la persistencia cifrada está activada. El resto son opcionales, y el Engine degrada de forma controlada sin ellos.

| Puerto                   | ¿Obligatorio?                             | Sin él                                                                                                                                        |
| ------------------------ | ----------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------- |
| `ConnectorRegistry`      | **Siempre**                               | `engine.New` falla y no existe ningún Engine                                                                                                  |
| `CredentialProtector`    | Solo con `WithEncryptedPersistence(true)` | La construcción falla cuando la persistencia cifrada está activada; las credenciales llegan al almacén sin protección cuando está desactivada |
| `ConnectionStore`        | Opcional                                  | Toda operación excepto `Limits()` falla                                                                                                       |
| `ResultSink`             | Opcional                                  | El modo Store no está disponible, y la extracción corre en modo Direct                                                                        |
| `SchemaCache`            | Opcional                                  | El descubrimiento de esquemas siempre va al datasource en vivo                                                                                |
| `ExecutionStore`         | Opcional                                  | Sin seguimiento durable del estado de ejecución                                                                                               |
| `ActiveExecutionChecker` | Opcional                                  | Sin control de conflictos en actualizaciones y borrados de conexiones                                                                         |
| `Observability`          | Opcional                                  | Los hooks de trazas quedan en no-op                                                                                                           |

La [referencia de puertos](/es/fetcher/fetcher-engine-ports) documenta cada contrato y cada degradación en detalle. Léela antes de decidir qué puertos omitir — "opcional" no significa "inofensivo".

Para tests y primeras corridas, el harness **`pkg/engine/memory`** provee implementaciones en memoria de los puertos de almacenamiento: el registro de conectores, el connection store, la caché de esquemas, el result sink y el execution store. No necesitas MongoDB, Redis, RabbitMQ ni object storage para ejercitar el Engine. El harness no incluye un `CredentialProtector`, así que un test que active la persistencia cifrada debe aportar uno.

## 3. Construye, planifica, ejecuta

***

Este ejemplo es autocontenido. Usa la implementación en memoria, así que corre con cero infraestructura.

```go theme={null}
package main

import (
	"context"
	"fmt"
	"log"

	"github.com/LerianStudio/fetcher/pkg/engine"
	"github.com/LerianStudio/fetcher/pkg/engine/memory"
)

func main() {
	ctx := context.Background()

	// Provide ports. In production these are your real adapters (see pkg/enginecompat);
	// here the in-memory harness stands in so the example runs with zero infrastructure.
	store := memory.NewConnectionStore()
	registry := memory.NewConnectorRegistry()

	// Construct the Engine. WithConnectorRegistry is the only required option.
	eng, err := engine.New(
		engine.WithConnectorRegistry(registry),
		engine.WithConnectionStore(store),
	)
	if err != nil {
		log.Fatal(err)
	}

	// Every operation is scoped to a tenant — the sole isolation dimension.
	tenant, err := engine.NewTenantContext("tenant-123")
	if err != nil {
		log.Fatal(err)
	}

	// Register a connector for the datasource type, then persist a connection.
	conn := memory.NewTemplateConnector(memory.ConnectorBehavior{
		Schema: engine.SchemaSnapshot{
			ConfigName: "pg-main",
			Tables:     []engine.TableSnapshot{{Name: "public.users", Fields: []string{"id", "email"}}},
		},
		Rows: map[string][]map[string]any{
			"public.users": {{"id": 1, "email": "a@example.com"}},
		},
	})
	registry.Register("postgres", memory.NewConnectorFactory(conn))

	if _, err = eng.CreateConnection(ctx, tenant, engine.NewConnectionInput(engine.ConnectionInputParams{
		ConfigName: "pg-main",
		Type:       "postgres",
		Host:       "localhost",
		Port:       5432,
	})); err != nil {
		log.Fatal(err)
	}

	// Plan validates the request against the live schema and enforces limits.
	plan, err := eng.PlanExtraction(ctx, tenant, engine.ExtractionRequest{
		MappedFields: map[string]engine.FieldSelection{
			"pg-main": {"public.users": {"id", "email"}},
		},
	})
	if err != nil {
		log.Fatal(err)
	}

	// Execute. With no ResultSink wired, the Engine runs in Direct mode and returns
	// inline JSON bytes plus a SHA-256 integrity digest.
	result, err := eng.ExecuteExtraction(ctx, plan)
	if err != nil {
		log.Fatal(err)
	}

	fmt.Printf("rows=%d bytes=%d\n", result.Direct.RowCount, len(result.Direct.Data))
}
```

### Qué muestra el ejemplo

* **Una sola opción obligatoria.** `WithConnectorRegistry` es el único puerto que `engine.New` exige. El almacén de conexiones aquí es una comodidad, pero si lo omites toda operación falla.
* **Validación en tiempo de construcción.** `engine.New` rechaza un puerto pasado como nil tipado, no solo como nil literal. Un host mal configurado falla en la construcción en lugar de entrar en pánico en el primer uso.
* **Alcance por tenant en cada llamada.** `NewTenantContext` construye la única dimensión de aislamiento que el Engine conoce. Lleva un tenant ID y un request ID opcional — ninguna organización y ningún producto.
* **Primero planifica, después ejecuta.** `PlanExtraction` valida la petición contra el esquema en vivo y aplica los límites. `ExecuteExtraction` lee el plan.
* **El modo sale de la composición.** El ejemplo no conecta ningún `ResultSink`, así que el Engine elige el modo Direct y devuelve los bytes inline con un digest SHA-256. Agrega un sink y el mismo código devuelve una referencia de storage. Consulta [Modo Direct y modo Store](/es/fetcher/fetcher-engine-overview#modo-direct-y-modo-store).

## Pasar a producción

***

Cambia la implementación en memoria por adaptadores reales. Los propios servicios de Fetcher son la implementación de referencia, y su código fuente es público. Ellos conectan los puertos del Engine a infraestructura real bajo `pkg/enginecompat`:

| Paquete adaptador                   | Conecta                                                                                                |
| ----------------------------------- | ------------------------------------------------------------------------------------------------------ |
| `pkg/enginecompat/connectioncompat` | Almacén de conexiones, acceso a conexiones, contexto de tenant y el verificador de ejecuciones activas |
| `pkg/enginecompat/schemacompat`     | Caché de esquemas, conector de esquemas y construcción de snapshots                                    |
| `pkg/enginecompat/datasource`       | Los drivers de datasource detrás del contrato de conector                                              |
| `pkg/enginecompat/tablenorm`        | Normalización de nombres de tabla calificados                                                          |

Lee cómo los conectan los servicios:

* **CRUD de conexiones** — `components/manager/internal/bootstrap/connection_engine.go`
* **Descubrimiento y caché de esquemas** — `components/manager/internal/bootstrap/schema_engine.go`
* **Planificar y ejecutar extracciones** — `components/worker/internal/bootstrap/extraction_engine.go`

Dos reglas pasan de la implementación en memoria a producción:

1. **Tus adaptadores son dueños del alcance por tenant.** El Engine pasa un contexto de tenant a cada llamada de puerto y aplica el límite en su propio borde. Un almacén que ignora el tenant ID filtra datos entre tenants, y el Engine no puede detectarlo por ti.
2. **Tus adaptadores son dueños de los secretos.** El Engine llama a `Protect` y `Reveal` y registra solo la versión de clave devuelta como metadato. La derivación, la rotación y el almacenamiento de claves se quedan en tu host.

## Próximos pasos

***

<CardGroup cols={2}>
  <Card title="Referencia de puertos" icon="plug" href="/es/fetcher/fetcher-engine-ports">
    Cada puerto, su contrato y el comportamiento que obtienes sin él.
  </Card>

  <Card title="Visión general del Engine" icon="cube" href="/es/fetcher/fetcher-engine-overview">
    El modelo de tres capas, la frontera de importación y los dos modos de resultado.
  </Card>
</CardGroup>
