Helpers

Manifest validation

Funzioni validateManifest e isValidManifest per validare a runtime payload non trusted come MFEManifest.

API

function validateManifest(manifest: unknown): {
  valid: boolean
  errors: string[]
}

function isValidManifest(manifest: unknown): manifest is MFEManifest

Quando usarle

Quando il manifest arriva da fonti non controllate (config remota, admin panel, JSON di API esterne, file caricato da utente). Il TypeScript da solo non protegge a runtime: un payload unknown deve essere validato prima di essere usato.

validateManifest

Ritorna { valid, errors } con dettagli di validazione. Adatto per mostrare errori all'utente o loggarli.

import { validateManifest } from '@pzeta/mfe-contracts/helpers'

const payload: unknown = JSON.parse(rawJson)

const { valid, errors } = validateManifest(payload)

if (!valid) {
  console.error('Manifest non valido:')
  errors.forEach((e) => console.error('  -', e))
  return
}

// Sicuro da usare (ma typecast esplicito)
const manifest = payload as MFEManifest

Cosa valida

CheckErrore
manifest non null/undefined e tipo object'manifest deve essere un oggetto non-null'
name stringa non vuota'manifest.name è richiesto e deve essere una stringa non vuota'
version stringa non vuota'manifest.version è richiesto e deve essere una stringa non vuota'
description stringa non vuota'manifest.description è richiesto e deve essere una stringa non vuota'
routes array'manifest.routes deve essere un array'
Per ogni routes[i]: oggetto'route[i] deve essere un oggetto'
routes[i].path stringa non vuota'route[i].path è richiesto e deve essere una stringa non vuota'
routes[i].label stringa non vuota'route[i].label è richiesto e deve essere una stringa non vuota'
Validazione shallow: campi permissions, components, tailwindStyles, metanon sono validati in dettaglio. Se ti servono check più stringenti, aggiungi una validazione custom dopo validateManifest.

isValidManifest (type guard)

Versione boolean adatta a if/assert:

import { isValidManifest } from '@pzeta/mfe-contracts/helpers'

if (isValidManifest(payload)) {
  // payload è ora typed come MFEManifest, senza cast
  console.log(payload.name, payload.version)
}

Internamente è un wrapper su validateManifest(payload).valid.

Esempio: fetch da config remota

import {
  validateManifest,
  isValidManifest,
} from '@pzeta/mfe-contracts/helpers'

async function fetchManifests(): Promise<MFEManifest[]> {
  const res = await fetch('/api/mfe-registry')
  const payloads = (await res.json()) as unknown[]

  const valid: MFEManifest[] = []

  for (const payload of payloads) {
    const { valid: isValid, errors } = validateManifest(payload)
    if (isValid) {
      valid.push(payload as MFEManifest)
    } else {
      console.warn(
        `Manifest scartato:\n  ${errors.join('\n  ')}`,
        payload,
      )
    }
  }

  return valid
}

Esempio: errori user-friendly

function validateAndReport(payload: unknown): MFEManifest | null {
  const { valid, errors } = validateManifest(payload)
  if (!valid) {
    toast.add({
      severity: 'error',
      summary: 'Manifest MFE non valido',
      detail: errors.join('\n'),
      life: 10000,
    })
    return null
  }
  return payload as MFEManifest
}

Pattern: validazione + sanitizzazione

validateManifest controlla la struttura ma non sanitizza il contenuto. Per CSS custom dichiarato in tailwindStyles.customClasses, usa anche sanitizeCss:

if (isValidManifest(payload)) {
  if (payload.tailwindStyles?.customClasses) {
    try {
      sanitizeCss(payload.tailwindStyles.customClasses)
    } catch (err) {
      console.error('CSS custom non sicuro:', err.message)
      delete payload.tailwindStyles.customClasses
    }
  }
  // usa payload
}

Tipi correlati