O sistema de importação do EmDash usa uma arquitetura de fontes plugáveis. Cada fonte sabe sondar, analisar e buscar conteúdo de uma plataforma específica.
Fontes de importação
| ID da fonte | Plataforma | Sonda | OAuth | Importação completa |
|---|---|---|---|---|
wxr | Arquivo de exportação WP | Não | Não | Sim |
wordpress-com | WordPress.com | Sim | Sim | Sim |
wordpress-rest | WordPress auto-hospedado | Sim | Não | Apenas sonda |
Upload de arquivo WXR
O método mais completo. Envie um arquivo de exportação WordPress eXtended RSS (WXR) diretamente no painel de administração.
Capacidades:
- Todos os tipos de post (incluindo personalizados)
- Todos os campos meta
- Rascunhos e posts privados
- Hierarquia taxonômica completa
- Metadados de anexos de mídia
Como obter um arquivo WXR:
- No admin do WordPress, vá em Ferramentas → Exportar
- Selecione Todo o conteúdo ou tipos de post específicos
- Clique em Baixar arquivo de exportação
- Envie o
.xmlpara o EmDash
OAuth WordPress.com
Para sites no WordPress.com, conecte via OAuth sem exportações manuais.
- Informe a URL do site WordPress.com
- Clique em Conectar com WordPress.com
- Autorize o EmDash no pop-up do WordPress.com
- Selecione o conteúdo a importar
O que inclui:
- Conteúdo publicado e rascunho
- Posts privados (com autorização)
- Arquivos de mídia via API
- Campos personalizados expostos na REST API
Sonda REST do WordPress
Ao informar uma URL, o EmDash sonda o site, detecta WordPress e mostra o conteúdo disponível:
Detected: WordPress 6.4
├── Posts: 127 (published)
├── Pages: 12 (published)
└── Media: 89 files
Note: Drafts and private content require authentication
or a full WXR export.
A sonda REST é informativa. Para importações completas, sugere upload WXR ou OAuth (WordPress.com).
Fluxo de importação
Todas as fontes seguem o mesmo fluxo:
┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ Connect │────▶│ Analyze │────▶│ Prepare │────▶│ Execute │
│ (probe/ │ │ (schema │ │ (create │ │ (import │
│ upload) │ │ check) │ │ schema) │ │ content) │
└─────────────┘ └─────────────┘ └─────────────┘ └─────────────┘
Etapa 1: Conectar
Informe uma URL para sondar ou envie um arquivo diretamente.
A sonda por URL executa todas as fontes registradas em paralelo. A correspondência com maior confiança determina a ação sugerida:
- Site WordPress.com → Oferecer conexão OAuth
- WordPress auto-hospedado → Mostrar instruções de exportação
- Desconhecido → Sugerir upload de arquivo
Etapa 2: Analisar
A fonte analisa o conteúdo e verifica compatibilidade de esquema:
Post Types:
├── post (127) → posts [New collection]
├── page (12) → pages [Existing, compatible]
├── product (45) → products [Add 3 fields]
└── revision (234) → [Skip - internal type]
Required Schema Changes:
├── Create collection: posts
├── Add fields to pages: featured_image
└── Create collection: products
Cada tipo de post mostra um status:
| Status | Significado |
|---|---|
| Ready | Coleção existe com campos compatíveis |
| New collection | Será criada automaticamente |
| Add fields | Coleção existe; campos faltantes adicionados |
| Incompatible | Conflitos de tipo de campo (correção manual) |
Etapa 3: Preparar esquema
Clique em Criar esquema e importar para:
- Criar novas coleções via SchemaRegistry
- Adicionar campos faltantes com tipos de coluna corretos
- Configurar tabelas de conteúdo com índices
Etapa 4: Executar importação
O conteúdo é importado em sequência:
- Gutenberg/HTML convertido para Portable Text
- Status WordPress mapeado para status EmDash
- Autores WordPress mapeados para propriedade (
authorId) e linhas de crédito na apresentação - Taxonomias criadas e vinculadas
- Blocos reutilizáveis (
wp_block) importados como Sections - Progresso em tempo real
Comportamento de importação de autores:
- Se o mapeamento de autor aponta para um usuário EmDash, a propriedade é definida para esse usuário e uma linha de crédito vinculada é criada/reutilizada para o mesmo usuário.
- Sem mapeamento de usuário, uma linha de crédito convidado é criada/reutilizada a partir da identidade do autor no WordPress.
- Entradas importadas recebem créditos de linha ordenados; o primeiro crédito é
primaryBylineId.
Etapa 5: Importar mídia (opcional)
Após o conteúdo, importe mídia opcionalmente:
-
Análise — Mostra contagens de anexos por tipo
Media found: ├── Images: 75 files ├── Video: 10 files └── Other: 4 files -
Download — Transmite das URLs WordPress com progresso
Importing media... ├── 45 of 89 (50%) ├── Current: vacation-photo.jpg └── Status: Uploading -
Reescrever URLs — O conteúdo é atualizado automaticamente com novas URLs
A importação de mídia usa hash de conteúdo (xxHash64) para deduplicação. A mesma imagem em vários posts é armazenada uma vez.
Interface da fonte
As fontes de importação implementam uma interface padrão:
interface ImportSource {
/** Unique identifier */
id: string;
/** Display name */
name: string;
/** Probe a URL (optional) */
probe?(url: string): Promise<SourceProbeResult | null>;
/** Analyze content from this source */
analyze(input: SourceInput, context: ImportContext): Promise<ImportAnalysis>;
/** Stream content items */
fetchContent(input: SourceInput, options: FetchOptions): AsyncGenerator<NormalizedItem>;
}
Tipos de entrada
As fontes aceitam entradas diferentes:
// File upload (WXR)
{ type: "file", file: File }
// URL with optional token (REST API)
{ type: "url", url: string, token?: string }
// OAuth connection (WordPress.com)
{ type: "oauth", url: string, accessToken: string }
Saída normalizada
Todas as fontes produzem o mesmo formato normalizado:
interface NormalizedItem {
sourceId: string | number;
postType: string;
status: "publish" | "draft" | "pending" | "private" | "future";
slug: string;
title: string;
content: PortableTextBlock[];
excerpt?: string;
date: Date;
author?: string;
authors?: string[];
categories?: string[];
tags?: string[];
meta?: Record<string, unknown>;
featuredImage?: string;
}
Endpoints da API
O sistema de importação expõe estes endpoints:
Sondar URL
POST /_emdash/api/import/probe
Content-Type: application/json
{ "url": "https://example.com" }
Retorna a plataforma detectada e a ação sugerida.
Analisar WXR
POST /_emdash/api/import/wordpress/analyze
Content-Type: multipart/form-data
file: [WordPress export .xml]
Retorna análise de tipos de post e compatibilidade de esquema.
Preparar esquema
POST /_emdash/api/import/wordpress/prepare
Content-Type: application/json
{
"postTypes": [
{ "name": "post", "collection": "posts", "enabled": true }
]
}
Cria coleções e campos.
Executar importação
POST /_emdash/api/import/wordpress/execute
Content-Type: multipart/form-data
file: [WordPress export .xml]
config: { "postTypeMappings": { "post": { "collection": "posts" } } }
Importa conteúdo para as coleções indicadas.
Importar mídia
POST /_emdash/api/import/wordpress/media
Content-Type: application/json
{
"attachments": [{ "id": 123, "url": "https://..." }],
"stream": true
}
Transmite atualizações de progresso NDJSON durante download/upload.
Reescrever URLs
POST /_emdash/api/import/wordpress/rewrite-urls
Content-Type: application/json
{
"urlMap": { "https://old.com/image.jpg": "/_emdash/media/abc123" }
}
Atualiza conteúdo Portable Text com novas URLs de mídia.
Tratamento de erros
Erros recuperáveis
- Timeout de rede — Nova tentativa com backoff
- Falha ao analisar um item — Registrado, ignorado, importação continua
- Falha no download de mídia — Marcado para tratamento manual
Erros fatais
- Formato de arquivo inválido — Importação para com mensagem
- Conexão com banco perdida — Importação pausa, permite retomar
- Cota de armazenamento excedida — Importação para, mostra uso
Relatório de erros
Após a importação:
Import Complete
✓ 125 posts imported
✓ 12 pages imported
✓ 85 media references recorded
⚠ 2 items had warnings:
- Post "Special Characters ñ" - title encoding fixed
- Page "About" - duplicate slug renamed to "about-1"
✗ 1 item failed:
- Post ID 456 - content parsing error (saved as draft)
Itens com falha são salvos como rascunhos com conteúdo original em _importError para revisão.
Fontes personalizadas
Crie uma fonte para outras plataformas:
import type { ImportSource } from "emdash/import";
export const mySource: ImportSource = {
id: "my-platform",
name: "My Platform",
description: "Import from My Platform",
icon: "globe",
canProbe: true,
async probe(url) {
// Check if URL matches your platform
const response = await fetch(`${url}/api/info`);
if (!response.ok) return null;
return {
sourceId: "my-platform",
confidence: "definite",
detected: { platform: "my-platform" },
// ...
};
},
async analyze(input, context) {
// Parse and analyze content
// Return ImportAnalysis
},
async *fetchContent(input, options) {
// Yield NormalizedItem for each content piece
for (const item of items) {
yield {
sourceId: item.id,
postType: "post",
title: item.title,
content: convertToPortableText(item.body),
// ...
};
}
},
};
Registre a fonte na configuração EmDash:
import { mySource } from "./src/import/custom-source";
export default defineConfig({
integrations: [
emdash({
import: {
sources: [mySource],
},
}),
],
});
Próximos passos
- WordPress Migration — Guia completo de migração WordPress
- Plugin Porting — Portar plugins WordPress para o EmDash