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
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 per parecchio tempo sono entrati nel motore come `any[]`. Il risultato era che ogni punto che li usava scriveva `viewModesDb as any[]` al momento della chiamata — un cast che sembra una precauzione e in realtà è una rinuncia ripetuta: nessuno dei consumatori sapeva davvero cosa ci fosse dentro.
`store.ts` dichiara `ViewModeDef` e `StyleDef` una volta, e i consumatori smettono di castare. 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 plausibiliengine/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 descrittoreengine/mapper/— quattro file: dai dati agli slotengine/styling/— tre file: dal viewMode alle classiengine/dal/— tredici file: la lettura cache-awareengine/utils/— tre file: estrazione delle props di UI e proiezione degli slot in React