Carmen Labs

Parser Contract

The signature, inputs, outputs, and constraints a conforming parser must satisfy.

Parser Contract

The parser transforms normalized input into a data model. It is the only place in an MView where "understanding the file" happens — the component downstream should never need to.

Signature

export default async function parse(input: any, context: any): Promise<unknown>;
  • input — the normalized representation of the source file, shaped according to the capability declared in accepts. See Capabilities for the exact shape per capability.
  • context — everything else the parser is allowed to know:
{
  input,      // normalized source data
  source,     // { path, contentHash } — metadata only, not a file handle
  settings,   // the manifest's settings, merged with any runtime overrides
  resources,  // resolved paths under resources/
  workspace   // workspace-level metadata (no arbitrary fs access)
}
  • Return value — any JSON-serializable data model. This becomes the data prop passed to the component.

Example

export default async function parse(input: any, context: any) {
  const rows = input.rows ?? [];
 
  const totalSales = rows.reduce((sum: number, row: any) => {
    return sum + Number(row.Total ?? row.total ?? 0);
  }, 0);
 
  const salesByMonth = rows.map((row: any) => ({
    month: row.Month ?? row.month,
    total: Number(row.Total ?? row.total ?? 0)
  }));
 
  const topClients = [...rows]
    .sort((a: any, b: any) => Number(b.Total ?? b.total ?? 0) - Number(a.Total ?? a.total ?? 0))
    .slice(0, context.settings?.topClientsLimit ?? 10);
 
  return { totalSales, salesByMonth, topClients };
}

Constraints (MUST / MUST NOT)

  • The parser MUST NOT read files directly. It only sees the normalized input it's handed.
  • The parser MUST NOT use absolute paths. Any path it produces or consumes is relative.
  • The parser MUST be a pure function of (input, context) — same inputs, same output. No reliance on hidden global or session state.
  • The parser MUST be re-executable: running it again after the source file changes should produce an updated, correct data model without manual intervention.
  • The parser SHOULD fail with a clear, catchable error rather than throwing something opaque when input doesn't match what it expects (e.g., an expected column is missing) — the runtime surfaces this to the user with an option to regenerate.

What the runtime guarantees the parser

  • input has already been normalized according to the capability the MView declared in accepts — the parser is not responsible for parsing raw file bytes.
  • context.source describes the file but does not grant filesystem access.
  • context.settings is the manifest's settings, already merged with any user overrides.

Relationship to the component

The parser's return value is opaque to the runtime — it's whatever shape the paired component expects. The Component Contract defines what happens next.

Contrato del parser

La firma, entradas, salidas y restricciones que debe cumplir un parser conforme.

Contrato del parser

El parser transforma la entrada normalizada en un modelo de datos. Es el único lugar de una MView donde ocurre la “comprensión del archivo”; el componente posterior no debería necesitar hacerlo.

Firma

export default async function parse(input: any, context: any): Promise<unknown>;
  • input — la representación normalizada del archivo fuente, con la forma correspondiente a la capacidad declarada en accepts. Ver Capacidades para la forma exacta por capacidad.
  • context — todo lo demás que el parser puede conocer:
{
  input,      // datos fuente normalizados
  source,     // { path, contentHash } — solo metadatos, no un file handle
  settings,   // settings del manifiesto, combinados con overrides del runtime
  resources,  // rutas resueltas bajo resources/
  workspace   // metadatos del workspace (sin acceso fs arbitrario)
}
  • Valor de retorno — cualquier modelo de datos serializable como JSON. Esto se convierte en la prop data que recibe el componente.

Ejemplo

export default async function parse(input: any, context: any) {
  const rows = input.rows ?? [];
 
  const totalSales = rows.reduce((sum: number, row: any) => {
    return sum + Number(row.Total ?? row.total ?? 0);
  }, 0);
 
  const salesByMonth = rows.map((row: any) => ({
    month: row.Month ?? row.month,
    total: Number(row.Total ?? row.total ?? 0)
  }));
 
  const topClients = [...rows]
    .sort((a: any, b: any) => Number(b.Total ?? b.total ?? 0) - Number(a.Total ?? a.total ?? 0))
    .slice(0, context.settings?.topClientsLimit ?? 10);
 
  return { totalSales, salesByMonth, topClients };
}

Restricciones (MUST / MUST NOT)

  • El parser MUST NOT leer archivos directamente. Solo ve el input normalizado que recibe.
  • El parser MUST NOT usar rutas absolutas. Toda ruta que produzca o consuma es relativa.
  • El parser MUST ser una función pura de (input, context): mismas entradas, misma salida. Sin depender de estado global o de sesión oculto.
  • El parser MUST ser reejecutable: ejecutarlo de nuevo después de que cambie el archivo fuente debe producir un modelo de datos actualizado y correcto sin intervención manual.
  • El parser SHOULD fallar con un error claro y capturable, en lugar de lanzar algo opaco cuando input no coincide con lo esperado (por ejemplo, falta una columna esperada); el runtime muestra esto al usuario con una opción para regenerar.

Lo que el runtime garantiza al parser

  • input ya fue normalizado según la capacidad que la MView declaró en accepts; el parser no es responsable de parsear bytes raw del archivo.
  • context.source describe el archivo, pero no concede acceso al filesystem.
  • context.settings son los settings del manifiesto, ya combinados con cualquier override del usuario.

Relación con el componente

El valor de retorno del parser es opaco para el runtime: tiene la forma que espera el componente emparejado. El Contrato del componente define lo que ocurre después.