WoodyLabBuild with UIMS

Documentazione · src/engine

Il motore

Sette file alla radice e quattro sottocartelle. Qui il dato diventa qualcosa che si può disegnare, e succede sul server — prima che React esista.

Un blocco è un dato con quattro chiavi lette per nome

Di tutto quello che un blocco contiene, il motore ne guarda quattro cose. blockType dice quale resolver lo tratta. viewMode nomina la forma. slot dice in quale apertura del genitore va proiettato. content tiene i figli. Tutto il resto — il testo, l’immagine, l’href, i campi che quel tipo di blocco ha — appartiene al blocco e il motore non lo interpreta: lo passa.

È una scelta con una conseguenza precisa: aggiungere un campo a un blocco non richiede di toccare il motore. Il campo finisce in data e arriva al componente. Il motore resta ignorante del dominio, che è la ragione per cui non cresce quando cresce il progetto.

engine/block.ts

il tipo UimsBlock: le quattro chiavi note più l’apertura al resto

La forma di un blocco

type UimsBlock = {
  blockType: string
  viewMode?: string
  slot?: string
  className?: string
  content?: UimsBlock[]
  uiState?: Record<string, unknown>
  [field: string]: unknown   // i campi propri del tipo di blocco
}

Il contesto: una mappa da tipo di blocco a resolver

createUIMSContext costruisce l’oggetto che il progetto tiene in uimsContext.ts e usa in ogni pagina: UIMS.resolve(block). Dentro non c’è nient’altro che una mappa da blockType alla funzione che lo tratta, più il default per tutti i tipi che non ne hanno uno dedicato.

Un resolver può restituire null, e questo è il dettaglio che evita una classe intera di crash: un sectionRef che punta a una sezione cancellata risolve a niente. Non è un errore, è la risposta giusta — la sezione non c’è. Il renderer salta il descrittore vuoto e la pagina esce lo stesso.

Il blocco in ingresso resta any, e non per pigrizia: il tipo di un blocco dipende dalla configurazione del progetto, che il pacchetto non conosce. La tipizzazione forte sta dall’altra parte, sul descrittore che esce.

engine/createUIMSContext.ts

costruisce il contesto: registro dei resolver, default, e resolve

engine/index.ts

la porta pubblica dell’area

Gli store, tipizzati una volta sola

db.viewmodes.json e db.styles.json sono le due sorgenti di forma, e hanno un tipo: entrano nel motore come ViewModeDef[] e StyleDef[], non come liste di qualcosa. Un cast al momento della chiamata sembrerebbe una precauzione ed è una rinuncia ripetuta: chi lo scrive dichiara di non sapere cosa ha in mano.

store.ts dichiara ViewModeDef e StyleDef in un punto solo, e ogni consumatore parte da lì. Il tipo descrive lo STORE, non la forma a runtime: quello che esce dal registry è già fuso con l’override del tema e normalizzato per il renderer, ed è un’altra cosa — descritta in engine/types.ts.

engine/store.ts

ViewModeDef e StyleDef: cosa contengono i due store

I tipi degli slot stanno dove servono

I quattro tipi che descrivono gli slot — la definizione, l’istanza di un figlio, lo slot risolto e la mappa di tutti — vivevano in react/type.ts, insieme alle props dei componenti. Da lì li importavano cinque file del motore: i tre mapper e le due utility di proiezione. La dipendenza andava nel verso sbagliato: il motore importava dal render.

Ora stanno in engine/types.ts, e react/type.ts li ri-esporta. Chi scrive un componente continua a importarli da @woodylab/uims/react, che è la porta giusta per lui; il motore non attraversa più il confine.

engine/types.ts

SlotDefinition, ChildInstance, ResolvedSlot, ResolvedSlots

Compatibilità: quali viewMode possono disegnare un blocco

Il motore è aperto per scelta: un viewMode è riferito per NOME, e niente vieta di accoppiare qualunque blocco a qualunque forma. Il costo di quell’apertura è il silenzio — dai al blocco icon un viewMode che nomina il componente Action e non succede niente di visibile, semplicemente non appare quello che ti aspettavi.

compatibility.ts non chiude l’apertura, la RENDE VISIBILE: sa quale componente ci si aspetta per un tipo di blocco e può dirlo. È materiale per l’interfaccia — l’admin propone prima i viewMode compatibili — non un divieto a runtime.

engine/compatibility.ts

la relazione blocco → viewMode plausibili

engine/resolver/compatibilityRuntime.ts

quale componente ci si aspetta per un tipo di blocco, calcolato al caricamento

Il vocabolario degli eventi: due insiemi, nessuno derivato dall’altro

Cosa può dire l’interfaccia è l’unione di due cose diverse. Gli HANDLER sono ciò che esiste e RISPONDE: quelli del pacchetto più quelli del progetto. Il catalogo db.events.json è ciò che è DICHIARATO: i nomi che l’admin propone in una tendina.

I due insiemi non coincidono e non devono. Un handler può esistere senza essere dichiarato — nessuno lo sceglie da interfaccia ma il codice lo può chiamare. Un nome può essere dichiarato senza handler — è il caso degli eventi di stato, che non hanno un handler perché li tratta il reducer. Derivare l’uno dall’altro avrebbe cancellato entrambi i casi legittimi.

engine/eventVocabulary.ts

l’unione dei due insiemi, per chi deve mostrare un elenco

Le quattro sottocartelle

Il resto del motore è diviso per passaggio: chi decide la forma di un blocco (resolver), chi porta i dati dentro gli slot (mapper), chi calcola le classi (styling), chi legge dal database con la cache giusta (dal). Più tre utility di proiezione che stanno in utils perché le usano tutti.

La dipendenza fra loro è a senso unico: styling non conosce nessuno, mapper usa styling, resolver usa entrambi. Era circolare finché i tre file di stile stavano dentro resolver/, ed è il motivo per cui sono usciti.

engine/resolver/

tredici file: dal blocco al descrittore

engine/mapper/

quattro file: dai dati agli slot

engine/styling/

tre file: dal viewMode alle classi

engine/dal/

tredici file: la lettura cache-aware

engine/utils/

tre file: estrazione delle props di UI e proiezione degli slot in React