@esfaenza/flow-builder 20.3.20 → 20.3.22

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,8 +1,8 @@
1
1
  # flow-builder
2
2
 
3
3
  Editor visuale per i flow descritti in [`FRONTEND.md`](../../FRONTEND.md): un componente
4
- **standalone Angular** che disegna il grafo, edita ogni tipo di elemento, valida, versiona e
5
- prova un flow.
4
+ **standalone Angular** che disegna il grafo, edita ogni tipo di elemento, valida, versiona un
5
+ flow e ne consegna l'esecuzione all'applicazione ospite.
6
6
 
7
7
  Requisiti: **Angular ≥ 18** (sviluppato e verificato su 19.2), TypeScript.
8
8
 
@@ -146,9 +146,10 @@ sono opzionali e rifiutano con `MissingService` se non sovrascritti.
146
146
  | `listEnumValues(enumType)` | opzionale — i **valori** di un tipo di enumerazione, cioe' i nomi scrivibili in `enumValue` (§4.2, §4.6). `listEnumTypes` e' l'altra meta' e non basta: quella dice quali tipi esistono, questa quali valori ha un tipo. `enumType` e' esattamente l'`objectType` della risorsa, del parametro o del **membro** di una classe (§4.7.1): la risposta e' del tipo, non del punto di uso, e la cache e' per nome del tipo. Lista vuota = "non lo so" — il valore si scrive a mano, e un tipo sconosciuto risponde vuoto, non 404 |
147
147
  | `listStructures`, `describeStructure(className)` | opzionali — le classi e i loro membri per le risorse `Structure` (§4.7). `listStructures` e' l'**elenco delle classi** senza membri; `describeStructure` restituisce la **descrizione** di una classe (`className`, `label`, `members`), non un array nudo (§4.7.1). Senza, le classi non sono verificabili e il nome si scrive a mano: "non lo so", non "non esiste". I tre esiti sono distinti: `members` popolato = elenco autorevole, `members: []` = membri non dichiarati (si digita, nessuna accusa), rifiuto `StructureNotFound` = classe non registrata (si segnala la **classe**) |
148
148
 
149
- | §6.5 Esecuzione | |
149
+ | Esecuzione dall'editor | |
150
150
  |---|---|
151
- | `startInterview`, `respondToScreen`, `resumeInterview`, `inspectInterview`, `abandonInterview` | opzionalisenza, il pannello «Prova» segnala che la funzione non c'e' |
151
+ | `runFlow(request)`, `debugFlow(request)` | opzionali — i comandi **Esegui** e **Debug** della barra. Non sono primitive del contratto: l'editor raccoglie i valori di ingresso in una finestra e passa la mano all'ospite, che sa dove gira il motore. Senza implementazione il comando fallisce con `MissingService` e l'editor lo dice non finge un avvio. Sono due metodi e non un flag perche' quasi sempre sono due strade diverse, e perche' un ambiente puo' esporre l'una senza l'altra. **Il ritorno e' facoltativo e conta**: un `FlowRunOutcome` finisce nel pannello «Debug» dell'editor stato, traccia, risorse, output — mentre `void` significa «guardo altrove». Restituirlo e' quasi obbligatorio sui flow **senza schermate**, dove non esiste nessun'altra interfaccia in cui vedere cos'e' successo |
152
+ | `startInterview`, `respondToScreen`, `resumeInterview`, `inspectInterview`, `abandonInterview` | opzionali — le primitive §6.5 dell'interview. **L'editor non le usa**: servono a chi implementa `runFlow`/`debugFlow` con un runner proprio (la demo lo fa) |
152
153
  | `completeStageStep(request)` | opzionale — conclude uno step di orchestrazione (§5.14) |
153
154
 
154
155
  **Errori.** Ogni metodo, in caso di rifiuto, deve fallire con un `FlowApiError`
@@ -191,6 +192,32 @@ l'attributo `style` del node a ogni spostamento e uno `[style.width]` verrebbe c
191
192
  tabella delle larghezze e' la stessa che il comando «Riordina» passa a dagre: se divergessero,
192
193
  i node larghi resterebbero sovrapposti.
193
194
 
195
+ **Copia, incolla, duplica.** `Ctrl`+`C` copia gli elementi selezionati — la selezione del canvas
196
+ puo' contenerne piu' d'uno (rettangolo, `Ctrl`+`A`) — `Ctrl`+`V` li incolla, `Ctrl`+`D` li duplica in
197
+ loco; gli stessi comandi stanno nella top bar («⧉» e «⎘») e sul node selezionato («⧉»). I tasti
198
+ ascoltano sull'host del builder e si ignorano quando il fuoco e' in un campo di testo, dove
199
+ `Ctrl`+`C` deve copiare il testo.
200
+
201
+ Cosa viaggia con il blocco: la configurazione intera dell'elemento, i **collegamenti interni** al
202
+ blocco (quelli verso l'esterno no — sarebbero `CONNECTOR_TARGET_UNKNOWN`) e le **risorse
203
+ referenziate**, che e' cio' che rende il gesto utile su un elemento complesso. Gli appunti stanno in
204
+ `localStorage`, quindi valgono anche fra flow diversi e fra schede: non e' la clipboard di sistema,
205
+ che per essere **letta** chiede un permesso che in un iframe puo' non arrivare mai.
206
+
207
+ L'incolla passa da una conferma perche' non e' mai una copia identica, e le tre cose che cambiano le
208
+ decide il contratto:
209
+
210
+ - i nomi già usati vengono **rinominati**, e non solo quello dell'elemento: uno spazio di nomi unico
211
+ (§3.3) comprende gli step di orchestrazione, i campi di uno screen dinamico e le screen action, e
212
+ duplicare uno screen dinamico senza rinominare i campi produceva un `NAME_DUPLICATED` per campo;
213
+ - i **riferimenti interni** al blocco seguono i nomi nuovi (§13.8: nessuna primitiva lo fa), condizioni,
214
+ `visibilityRule`, trigger e output automatici compresi;
215
+ - le risorse che nella destinazione non esistono si creano (con una casella per non farlo), quelle
216
+ omonime si **riusano**, e cio' che resta irrisolvibile — un elemento rimasto nel flow di origine —
217
+ viene elencato invece di sparire in un `REFERENCE_UNKNOWN` da scoprire dopo.
218
+
219
+ Tutto l'incolla e' **un solo passo di annulla**.
220
+
194
221
  **Dettaglio dell'elemento.** Con `inspectorMode="dialog"` (predefinito) il form si apre in una
195
222
  finestra sopra il canvas: doppio click sul node, bottone «✎» sul node, comando «Dettaglio»
196
223
  nella top bar, click su un rilievo nel pannello dei problemi, o creazione di un elemento dalla
@@ -479,13 +506,32 @@ versione **salvata** e le modifiche appena scritte spariscono senza dirlo. La st
479
506
  ripiego dove `cloneFlow` non c'e' (e' opzionale). Un nome già usato lo rifiuta il backend con
480
507
  `AlreadyExists`, e il modulo resta aperto sul nome da correggere.
481
508
 
482
- **Prova.** Interview con form generato dalle variabili di input, form generico per gli screen
483
- (valori in ingresso più i campi da restituire), `canGoBack` / `canFinish` / `canPause` come
484
- verita' runtime, traccia navigabile e tabella delle risorse con l'avviso in chiaro che la
485
- traccia riporta anche i dati personali. Su un'orchestrazione sospesa distingue le due attese
486
- (`isWaitingForEvent` / `isWaitingForStageStep`) e consente di concludere uno step al posto
487
- dell'assegnatario, rifiuto compreso: la risposta puo' portare una `interviewKey` **nuova**, e il
488
- pannello sostituisce quella vecchia invece di conservarla.
509
+ **Esegui e Debug.** Due comandi nella barra e una finestra: il form dei valori di ingresso,
510
+ generato dalle variabili `isInput` del documento tipo dichiarato rispettato anche in scrittura,
511
+ istanze di classe compilate un membro alla volta (§4.7), enum per **nome** da `listEnumValues`
512
+ (§4.6). Un campo lasciato vuoto non viene mandato: non valorizzato e stringa vuota sono cose
513
+ diverse. Poi l'editor **passa la mano**: chiama `runFlow` o `debugFlow` e non esegue niente per
514
+ conto suo. Si esegue la versione **salvata**, quindi i comandi sono spenti su un flow mai scritto
515
+ e la finestra avverte quando ci sono modifiche non salvate. Senza implementazione — categoria
516
+ `MissingService` — il banner dice quale delle due primitive manca, con parole comprensibili a chi
517
+ ha appena premuto «Esegui».
518
+
519
+ **Il pannello «Debug».** Cosa l'ospite ha restituito dall'ultima esecuzione: stato, output,
520
+ traccia degli elementi eseguiti — ogni riga porta all'elemento sul canvas — e risorse. Esiste per
521
+ un caso che altrimenti non ha risposta: un flow **senza schermate** (`AutoLaunched`, o
522
+ un'orchestrazione tutta in background) non apre nessuna interfaccia del runtime, quindi senza
523
+ questo pannello non ci sarebbe **nessun** posto in cui vedere cosa e' successo. `trace` e
524
+ `resources` arrivano solo con `debug: true` (§6.5), e riportano i valori di tutte le risorse —
525
+ dati personali compresi: l'avviso e' in chiaro. Il pannello non conduce niente: guarda, e basta.
526
+
527
+ Come si implementa l'altra metà si vede nella demo
528
+ (`projects/demo/src/app/runner`), e la scelta interessante e' **quando** aprire una finestra: un
529
+ flow che non ha niente da chiedere finisce da solo e l'esito torna subito all'editor, mentre uno
530
+ interattivo apre il runner e restituisce l'ultimo risultato alla chiusura — così la traccia resta
531
+ comunque lì dove si stava lavorando. Il runner guida l'interview con le primitive della §6.5,
532
+ distingue le due attese di `Suspended` (`isWaitingForEvent` / `isWaitingForStageStep`), conclude
533
+ uno step al posto dell'assegnatario — rifiuto compreso, con la `interviewKey` nuova che sostituisce
534
+ la vecchia — e mostra traccia e risorse solo in debug, dove riportano anche i dati personali.
489
535
 
490
536
  **Import/export.** Export JSON indentato; import via `POST /flows/parse`, che normalizza il
491
537
  documento o lo rifiuta con `InvalidDefinition`.
@@ -507,6 +553,10 @@ src/lib/
507
553
  "questo ambiente la primitiva non ce l'ha"
508
554
  flow-editor-session.ts versioni, salvataggio, concorrenza ottimistica
509
555
  flow-layout.service.ts auto-layout con dagre
556
+ flow-clipboard.ts copia/incolla: l'elenco unico dei campi-riferimento, la riscrittura
557
+ dei nomi, il piano dell'incolla (che nomi cambiano, che risorse
558
+ mancano, cosa resta irrisolto)
559
+ flow-clipboard.service.ts gli appunti condivisi fra istanze e schede (localStorage)
510
560
  flow-name.util.ts nomi: regexp, namespace unico, slug dalla label
511
561
  condition-logic.util.ts riscrittura di conditionLogic su cancella / riordina
512
562
  condition-types.util.ts quali tipi si confrontano, e i numeri in cultura invariante
@@ -514,7 +564,7 @@ src/lib/
514
564
  screen-field.util.ts l'albero dei campi di uno screen dinamico: percorsi, nomi, mutazioni
515
565
  ui/
516
566
  flow-builder.component.ts il componente da montare
517
- canvas/ palette/ inspector/ resources/ problems/ versions/ debug/ shared/
567
+ canvas/ palette/ inspector/ resources/ problems/ versions/ run/ debug/ shared/
518
568
  styles/
519
569
  flow-builder.css variabili del tema, controlli di form, stili degli archi
520
570
  ```
@@ -605,7 +655,7 @@ distribuire e nessuna dipendenza da un font di icone dell'applicazione ospite.
605
655
  | 9 | `Step` ed `Experiment` non sono supportati; `OrchestratedStage` lo e' | I due non supportati sono filtrati dalla palette via `isSupported` e, se arrivano da un import, l'inspector li mostra in sola lettura spiegando perche'. Lo stage ha il suo form: step non presentati come sequenza, e la condizione che referenzia l'output di un altro step segnalata con la via d'uscita |
606
656
  | 10 | Un `Delete` senza filtri e' un **errore**, un `Update` senza filtri un **avviso** | `RecordWriteInspectorComponent`: severita' diverse, e l'update di massa chiede una conferma esplicita |
607
657
  | 11 | Non cablare enum e operatori | Ogni tendina legge da `getDictionaries()`; le mappe locali intervengono solo come fallback quando il dizionario non e' disponibile |
608
- | 12 | Il **token di continuazione e' opaco** | `DebugPanelComponent` lo rimanda tale e quale, non lo legge e non lo mette in nessun URL |
658
+ | 12 | Il **token di continuazione e' opaco** | Non lo tocca nessuno nella libreria: l'editor non esegue, quindi il token nasce e muore nel runner dell'ospite. Quello della demo (`projects/demo/src/app/runner/interview-runner.component.ts`) lo rimanda tale e quale, non lo legge e non lo mette in nessun URL |
609
659
  | 13 | **Testo e numero non si confrontano** (`CONDITION_TYPE_MISMATCH`) | `ConditionEditorComponent` conosce il tipo del lato sinistro da `POST /flows/references`: filtra gli operatori applicabili, guida il campo del letterale del secondo operando e segnala la coppia vietata. La regola sta in `core/condition-types.util.ts`, che salta gli operatori con semantica propria (`Contains`, `In`, …). I numeri si scrivono in cultura invariante: `1234,50` viene tradotto in `1234.50`, non troncato |
610
660
  | 14 | **`None` non e' un operatore, e' un "da completare"** | Condizione evidenziata come incompleta, con il conteggio in testa alla sezione e la frase «la bozza si salva, l'attivazione no»; il validatore della demo lo emette come **errore** |
611
661
  | 15 | **Un valore data senza `Z` significa ora locale** | `ValueEditorComponent` ha un selettore «Ora locale / UTC (Z)», dice che cambiare fuso riscrive l'orario e non lo converte, e toglie il suffisso solo per il controllo `datetime-local`, che con il fuso resterebbe vuoto |
@@ -643,7 +693,7 @@ possibile anche con errori e attivazione bloccata dagli errori (§8).
643
693
  (`elementFromPoint` per il contenitore piu' interno, i rettangoli dei fratelli per la
644
694
  posizione).
645
695
  - **L'esecutore della demo non valuta le condizioni**: prende il primo ramo disponibile e lo
646
- scrive nella traccia. È un mock per esercitare il pannello, non il motore.
696
+ scrive nella traccia. È un mock per esercitare il runner dell'ospite, non il motore.
647
697
  - **Nessun test automatico**: la libreria e' stata verificata a mano sull'app demo (canvas,
648
698
  inspector, salvataggio, attivazione, interview completa). Casi su `condition-logic.util`,
649
699
  `flow-name.util`, `element-outlets` e `FlowDocumentStore` sarebbero il primo investimento