@esfaenza/dpe-builder 20.3.14 → 20.3.15
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.
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"esfaenza-dpe-builder.mjs","sources":["../../../projects/dpe-builder/src/lib/api/dpe-api-error.ts","../../../projects/dpe-builder/src/lib/api/dpe-builder-api.ts","../../../projects/dpe-builder/src/lib/api/http-dpe-builder-api.ts","../../../projects/dpe-builder/src/lib/model/dpe-definition.model.ts","../../../projects/dpe-builder/src/lib/model/dpe-diagnostics.model.ts","../../../projects/dpe-builder/src/lib/model/dpe-api.model.ts","../../../projects/dpe-builder/src/lib/core/dpe-capabilities.ts","../../../projects/dpe-builder/src/lib/core/dpe-graph.ts","../../../projects/dpe-builder/src/lib/core/dpe-node-collection-guard.ts","../../../projects/dpe-builder/src/lib/core/dpe-diagnostics.util.ts","../../../projects/dpe-builder/src/lib/core/dpe-node-layout.ts","../../../projects/dpe-builder/src/lib/core/dpe-node-catalog.ts","../../../projects/dpe-builder/src/lib/core/dpe-node-factory.ts","../../../projects/dpe-builder/src/lib/core/dpe-document.store.ts","../../../projects/dpe-builder/src/lib/core/dpe-catalog.store.ts","../../../projects/dpe-builder/src/lib/core/dpe-conditional-fields.ts","../../../projects/dpe-builder/src/lib/core/dpe-formula-catalog.view.ts","../../../projects/dpe-builder/src/lib/core/dpe-validation.service.ts","../../../projects/dpe-builder/src/lib/core/dpe-formula-catalog.store.ts","../../../projects/dpe-builder/src/lib/core/dpe-layout.service.ts","../../../projects/dpe-builder/src/lib/core/dpe-metadata.store.ts","../../../projects/dpe-builder/src/lib/core/dpe-node-icons.ts","../../../projects/dpe-builder/src/lib/core/dpe-viewport.ts","../../../projects/dpe-builder/src/lib/ui/shared/dpe-node-icon.component.ts","../../../projects/dpe-builder/src/lib/ui/canvas/dpe-canvas.model.ts","../../../projects/dpe-builder/src/lib/ui/canvas/dpe-canvas.component.ts","../../../projects/dpe-builder/src/lib/ui/canvas/dpe-canvas.component.html","../../../projects/dpe-builder/src/lib/ui/diagnostics/dpe-diagnostics-panel.component.ts","../../../projects/dpe-builder/src/lib/ui/inspector/dpe-node-facts.component.ts","../../../projects/dpe-builder/src/lib/ui/shared/dpe-select-value.directive.ts","../../../projects/dpe-builder/src/lib/ui/inspector/dpe-node-inspector-base.ts","../../../projects/dpe-builder/src/lib/ui/inspector/dpe-aggregate-inspector.component.ts","../../../projects/dpe-builder/src/lib/ui/inspector/dpe-append-inspector.component.ts","../../../projects/dpe-builder/src/lib/ui/inspector/dpe-atomic-writeback-inspector.component.ts","../../../projects/dpe-builder/src/lib/ui/shared/dpe-column-type-editor.component.ts","../../../projects/dpe-builder/src/lib/ui/inspector/dpe-custom-node-inspector.component.ts","../../../projects/dpe-builder/src/lib/ui/inspector/dpe-data-source-inspector.component.ts","../../../projects/dpe-builder/src/lib/ui/inspector/dpe-filter-inspector.component.ts","../../../projects/dpe-builder/src/lib/ui/inspector/dpe-forecast-inspector.component.ts","../../../projects/dpe-builder/src/lib/ui/inspector/dpe-hierarchy-path-inspector.component.ts","../../../projects/dpe-builder/src/lib/ui/inspector/dpe-join-inspector.component.ts","../../../projects/dpe-builder/src/lib/ui/shared/dpe-expression-editor.component.ts","../../../projects/dpe-builder/src/lib/ui/inspector/dpe-transform-inspector.component.ts","../../../projects/dpe-builder/src/lib/ui/inspector/dpe-writeback-inspector.component.ts","../../../projects/dpe-builder/src/lib/ui/inspector/dpe-node-inspector.component.ts","../../../projects/dpe-builder/src/lib/ui/dialog/dpe-node-dialog.component.ts","../../../projects/dpe-builder/src/lib/ui/palette/dpe-node-palette.component.ts","../../../projects/dpe-builder/src/lib/ui/preview/dpe-sql-preview.component.ts","../../../projects/dpe-builder/src/lib/ui/schema/dpe-schema-panel.component.ts","../../../projects/dpe-builder/src/lib/ui/dpe-builder.component.ts","../../../projects/dpe-builder/src/lib/ui/dpe-builder.component.html","../../../projects/dpe-builder/src/lib/ui/catalog/dpe-catalog.component.ts","../../../projects/dpe-builder/src/lib/ui/catalog/dpe-catalog.component.html","../../../projects/dpe-builder/src/public-api.ts","../../../projects/dpe-builder/src/esfaenza-dpe-builder.ts"],"sourcesContent":["/**\r\n * Errore categorizzato del contratto verso il backend.\r\n *\r\n * L'editor si comporta in base alla **categoria**, mai leggendo il messaggio: chi implementa\r\n * {@link DpeBuilderApi} traduce l'errore di trasporto (status HTTP, payload, timeout) in un\r\n * `DpeApiError`, e da li' in poi nella libreria non esiste piu' nessuna nozione di HTTP\r\n * (ADR 0007).\r\n */\r\n\r\n/**\r\n * Le categorie sono **tre** e chiuse, perche' sono tre comportamenti diversi della UI:\r\n *\r\n * - `MissingService`: la primitiva non e' implementata su questo ambiente. Degrada il\r\n * **singolo comando** (l'anteprima SQL si disabilita), non l'editor;\r\n * - `Transport`: la chiamata non e' arrivata o non e' tornata. Si puo' ritentare;\r\n * - `Backend`: il backend ha risposto rifiutando. Non si ritenta da soli.\r\n *\r\n * Un errore di validazione **non e'** una categoria: le diagnostiche sono un *risultato*\r\n * (`DpeValidateResult.diagnostics`), non un fallimento — e' la stessa regola del backend,\r\n * dove il fallimento previsto e' un risultato e non un'eccezione.\r\n */\r\nexport type DpeApiErrorCategory = 'MissingService' | 'Transport' | 'Backend';\r\n\r\nexport class DpeApiError extends Error {\r\n constructor(\r\n readonly category: DpeApiErrorCategory,\r\n message: string,\r\n /** Payload di dettaglio dell'host, opaco per la libreria. */\r\n readonly details?: unknown,\r\n ) {\r\n super(message);\r\n this.name = 'DpeApiError';\r\n }\r\n\r\n static is(value: unknown): value is DpeApiError {\r\n return value instanceof DpeApiError;\r\n }\r\n\r\n /** Vero quando il comando corrispondente va disabilitato invece che segnalato. */\r\n static isMissingService(value: unknown): boolean {\r\n return DpeApiError.is(value) && value.category === 'MissingService';\r\n }\r\n}\r\n\r\n/** Messaggi di ripiego: il `message` dell'host, quando c'e', ha la precedenza. */\r\nexport const DPE_API_ERROR_FALLBACK_MESSAGE: Record<DpeApiErrorCategory, string> = {\r\n MissingService: 'Funzione non configurata su questo ambiente.',\r\n Transport: 'Il backend non e’ raggiungibile.',\r\n Backend: 'Il backend ha rifiutato la richiesta.',\r\n};\r\n","/**\r\n * L'unica porta verso il backend — `docs/contracts.md` §7.\r\n *\r\n * **Non esiste un documento di endpoint: il contratto e' questa classe astratta** (ADR 0007).\r\n * Fa anche da token di dependency injection: chi integra l'editor fornisce una\r\n * implementazione e la registra come provider di `DpeBuilderApi`.\r\n *\r\n * Due conseguenze da non aggirare:\r\n *\r\n * 1. **questo file non conosce HTTP.** Nessun `HttpClient`, nessun `fetch`, nessun URL qui ne'\r\n * in nessuno store o componente. L'ADR 0020 ha spostato il client HTTP **dentro** la libreria\r\n * (`lib/api/http-dpe-builder-api.ts`), quindi la regola non e' piu' «zero HTTP in\r\n * `projects/dpe-builder`» ma «zero HTTP fuori da quel file»: e' *una* implementazione fra le\r\n * possibili, non la definizione del contratto, che resta questa classe;\r\n * 2. **un dato nuovo dal backend e' un metodo nuovo qui**, mai una chiamata dentro un\r\n * componente.\r\n *\r\n * I metodi con implementazione di default rifiutano con `DpeApiError('MissingService', ...)`:\r\n * un ambiente che non li espone lascia l'editor funzionante con il singolo comando\r\n * disabilitato. Un'anteprima SQL non disponibile disabilita l'anteprima, non l'editor.\r\n *\r\n * I metodi restituiscono `Observable` perche' l'I/O del frontend e' asincrono per natura. Il\r\n * vincolo di sincronia della libreria C# riguarda il backend: qui si traduce solo nel fatto\r\n * che una chiamata occupa il chiamante finche' non termina, quindi chi la invoca mostra\r\n * l'attesa e non incatena una validazione a ogni battitura senza debounce.\r\n */\r\n\r\nimport { Observable, throwError } from 'rxjs';\r\n\r\nimport { DpeDefinition } from '../model/dpe-definition.model';\r\nimport {\r\n DpeCompileResult,\r\n DpeDefinitionSummary,\r\n DpeEnvironment,\r\n DpeFormulaCatalogResult,\r\n DpeParameterValue,\r\n DpeValidateResult,\r\n TableLookupResult,\r\n TableSummary,\r\n} from '../model/dpe-api.model';\r\nimport { DpeApiError } from './dpe-api-error';\r\n// Import di **solo tipo**: `core/dpe-capabilities` importa questa classe, e senza `import type`\r\n// il ciclo sarebbe reale a runtime. Il tipo si cancella in compilazione, il ciclo no.\r\nimport type { DpeOptionalPrimitive } from '../core/dpe-capabilities';\r\n\r\n/** Ripiego dei metodi opzionali non implementati dall'host. */\r\nfunction missing(primitive: string): Observable<never> {\r\n return throwError(\r\n () => new DpeApiError('MissingService', `Primitiva non disponibile su questo ambiente: ${primitive}.`),\r\n );\r\n}\r\n\r\nexport abstract class DpeBuilderApi {\r\n /**\r\n * Primitive opzionali che **questo ambiente non serve**, dichiarate dall'ospite (ADR 0020,\r\n * `contracts.md` §7).\r\n *\r\n * Perche' esiste, dato che la rilevazione per identita' del metodo bastava: da quando la\r\n * libreria spedisce un client HTTP che implementa **tutte** le primitive\r\n * (`HttpDpeBuilderApi`), chi lo eredita non ha modo di *dis*-sovrascrivere un metodo. Si\r\n * ritroverebbe comandi attivi su endpoint che il suo backend non serve — esattamente il\r\n * difetto che la regola dell'identita' esisteva per prevenire.\r\n *\r\n * E' **solo sottrattiva**: non rende disponibile cio' che non e' implementato, e il tipo\r\n * `DpeOptionalPrimitive` le impedisce di nominare le tre primitive obbligatorie. Vuoto per\r\n * default, cosi' un'implementazione che non la tocca conserva esattamente il comportamento\r\n * di prima.\r\n *\r\n * Chi la usa deve anche rigettare la primitiva con `DpeApiError('MissingService', ...)`\r\n * **senza emettere la richiesta**: sapere in anticipo che un servizio non c'e' e chiederglielo\r\n * comunque e' il difetto che questa dichiarazione evita.\r\n */\r\n readonly unsupportedPrimitives: readonly DpeOptionalPrimitive[] = [];\r\n\r\n /**\r\n * `IDpeService.Validate` — **non produce SQL e non legge dati** (`contracts.md` §5). E' la\r\n * primitiva del feedback durante l'editing: diagnostiche, schemi di ogni nodo, ordine di\r\n * esecuzione e sequenza dei writeback in una sola risposta.\r\n *\r\n * Prende la definizione **in lavorazione**, non quella salvata: cio' che l'editor mostra\r\n * deve riferirsi a cio' che l'utente sta scrivendo. Va invocata con debounce.\r\n *\r\n * **`schemas` porta anche `requiredForm`**, la forma che i siti di espressione di quel nodo\r\n * pretendono. Sta li' e non sul risultato del catalogo delle formule perche' e' cio' che la\r\n * libreria **deriva** sul nodo, come le colonne e la compilabilita', mentre il catalogo e' cio'\r\n * che il compilatore **offre** e arriva solo quando il compilatore risponde: un ospite che\r\n * dichiara `providesFormulaCatalog` falso non fa chiamare quella rotta, e la forma non arrivava\r\n * mai. Chi implementa questo metodo la valorizza: senza, un campo formula non dice cosa vuole e\r\n * non si blocca quando il compilatore non sa produrre quella forma.\r\n */\r\n abstract validate(definition: DpeDefinition): Observable<DpeValidateResult>;\r\n\r\n /**\r\n * `IDpeService.GetTables` — passaggio diretto al provider di metadati dell'host, **non ai\r\n * cataloghi del database** (`contracts.md` §2). L'elenco puo' essere grande: chi lo consuma\r\n * carica in modo asincrono e filtra, non lo riversa in un `<select>`.\r\n */\r\n abstract getTables(): Observable<TableSummary[]>;\r\n\r\n /**\r\n * `IDpeService.GetTable` — i **tre** esiti dell'ADR 0004 restano distinti fino alla UI:\r\n * `Declared`, `DeclaredWithoutFields` (\"la tabella c'e', i campi non li so\") e `Unknown`.\r\n * Appiattirli e' esattamente cio' che il contratto vieta.\r\n */\r\n abstract getTable(tableName: string): Observable<TableLookupResult>;\r\n\r\n /**\r\n * `IDpeService.Compile` — l'anteprima dell'SQL generato, in sola lettura.\r\n *\r\n * Opzionale: senza di essa il comando di anteprima si disabilita e il resto dell'editor\r\n * continua a funzionare.\r\n *\r\n * `parameterValues` e' opzionale e corrisponde a `DpeCompileRequest.ParameterValues`.\r\n * Ometterlo e' un comportamento **dichiarato**, non un ripiego: l'anteprima usa allora i\r\n * `defaultValue` dei parametri. Passarli serve perche' senza di essi l'anteprima resterebbe\r\n * muta proprio sui parametri **privi** di default, cioe' quelli che l'utente vuole provare.\r\n */\r\n compilePreview(\r\n definition: DpeDefinition,\r\n parameterValues?: DpeParameterValue[],\r\n ): Observable<DpeCompileResult> {\r\n void definition;\r\n void parameterValues;\r\n return missing('compilePreview');\r\n }\r\n\r\n /**\r\n * Caricamento di una definizione per nome. Opzionale: dove non c'e', l'editor lavora sulla\r\n * definizione che gli viene passata dall'ospite.\r\n *\r\n * La deserializzazione (`IDpeService.Deserialize`) resta del backend: qui arriva un\r\n * documento gia' letto, con le sue eventuali diagnostiche gia' emesse.\r\n */\r\n load(name: string): Observable<DpeDefinition> {\r\n void name;\r\n return missing('load');\r\n }\r\n\r\n /** Salvataggio. Opzionale: un editor in sola lettura e' un uso legittimo. */\r\n save(definition: DpeDefinition): Observable<void> {\r\n void definition;\r\n return missing('save');\r\n }\r\n\r\n /**\r\n * La dichiarazione dell'ambiente (ADR 0017). Oggi porta **solo** il dialetto del database di\r\n * destinazione, che e' un fatto dell'installazione e non una preferenza dell'autore della\r\n * definizione: per questo l'editor non offre nessun comando per cambiarlo.\r\n *\r\n * Cosa l'editor fa con la risposta, e cosa non fa: mostra il dialetto in sola lettura e, se\r\n * differisce da quello della definizione (ADR 0009: e' li' che vive, ed e' da li' che il\r\n * backend lo legge), **lo segnala senza correggere**. Riscrivere `definition.dialect` sarebbe\r\n * una migrazione silenziosa delle capacita' con cui la definizione verra' compilata, per giunta eseguita\r\n * all'apertura e capace di marcare come «modificata» una definizione che nessuno ha toccato.\r\n *\r\n * Opzionale come le altre: dove manca, l'editor mostra il dialetto della definizione e non\r\n * dice nient'altro.\r\n */\r\n getEnvironment(): Observable<DpeEnvironment> {\r\n return missing('getEnvironment');\r\n }\r\n\r\n /**\r\n * `IDpeService.GetFormulaCatalog` — cosa il linguaggio di formule mette a disposizione **nel\r\n * nodo indicato** (ADR 0031/0036). Opzionale: senza, l'editor mostra un campo di testo semplice,\r\n * che e' esattamente cio' che mostrava prima che il catalogo esistesse.\r\n *\r\n * **Prende la definizione e il nome del nodo, non uno schema.** Il catalogo dipende dallo schema\r\n * a monte — i simboli `Column` sono le colonne che quel nodo vede — e risolvere uno schema e' del\r\n * backend (**I4**): il frontend non sa farlo e non deve imparare. La definizione e' quella **in\r\n * lavorazione**, perche' quella aperta nell'editor puo' non essere ancora salvata.\r\n *\r\n * **Non e' una chiamata leggera** (`frontend.md` §5-quater, normativo): lato libreria costa una\r\n * passata completa di grafo e di propagazione, e interroga il provider di metadati per tutti i\r\n * datasource della definizione. Chi la consuma la chiede **una volta per nodo** e la conserva —\r\n * `DpeFormulaCatalogStore` lo fa — e non a ogni apertura di un campo, che e' il modo naturale di\r\n * scriverlo e quello sbagliato.\r\n *\r\n * Si interroga `DpeEnvironment.providesFormulaCatalog` **prima** di chiamare: un compilatore che\r\n * non descrive il proprio linguaggio risponde un catalogo vuoto, e farsi pagare quella passata di\r\n * grafo per riceverlo e' spreco puro (ADR 0036 §5).\r\n *\r\n * **Restituisce l'involucro, non il catalogo nudo** (`contracts.md` §7). Il service distingue\r\n * tre esiti e il filo ne consegnava due, quindi un dialetto non coperto e un backend rotto\r\n * arrivavano qui identici: la differenza fra «non lo so» e «non si puo'» si perdeva prima di\r\n * raggiungere lo schermo. Chi implementa questo metodo non trasforma un esito con diagnostiche\r\n * in un errore di trasporto: le diagnostiche **sono** la risposta.\r\n *\r\n * **`requiredForm` non e' piu' in questa risposta**: sta su `DpeNodeSchemaInfo.requiredForm`,\r\n * cioe' negli schemi di `validate`. La ragione e' proprio la riga qui sopra — questa risposta\r\n * arriva solo quando il compilatore parla, e la forma la libreria la sa senza chiedere niente a\r\n * nessuno. Chi implementa questo metodo non la rimette qui: due copie dello stesso fatto\r\n * divergono (ADR 0018).\r\n */\r\n getFormulaCatalog(\r\n definition: DpeDefinition,\r\n nodeName: string,\r\n ): Observable<DpeFormulaCatalogResult> {\r\n void definition;\r\n void nodeName;\r\n return missing('getFormulaCatalog');\r\n }\r\n\r\n /**\r\n * Elenco delle definizioni disponibili — **catalogo** (ADR 0019). Opzionale: dove manca, un\r\n * catalogo montato dall'ospite mostra il perche' invece di un elenco vuoto.\r\n *\r\n * **Nessun oggetto di interrogazione**, di proposito: niente ricerca, niente paginazione. Il\r\n * filtro di testo del catalogo e' locale sui sommari ricevuti, e aggiungere una query in\r\n * seguito e' additivo. Un parametro che nessuno esercita e' un parametro che nessuno verifica.\r\n *\r\n * L'ospite e' tenuto a restituire i sommari in un **ordine stabile**: il catalogo non riordina,\r\n * perche' riordinare lato client combatterebbe una scelta che l'ospite ha gia' fatto.\r\n */\r\n listDefinitions(): Observable<DpeDefinitionSummary[]> {\r\n return missing('listDefinitions');\r\n }\r\n\r\n /**\r\n * Creazione di una definizione. Opzionale.\r\n *\r\n * **Restituisce il sommario, non `void`**: l'ospite puo' aver normalizzato il nome, e chi crea\r\n * apre **il nome tornato**, non quello digitato. Il risultato di una scrittura non si crede\r\n * sulla parola.\r\n *\r\n * Distinta da `save` per una ragione di merito: `save(definition)` significa «scrivi questa\r\n * definizione» e non distingue inserimento da aggiornamento, quindi farle significare anche\r\n * «creala» toglierebbe all'ospite l'unico posto in cui puo' **rifiutare un nome duplicato**.\r\n *\r\n * Non esiste una primitiva di duplicazione: duplicare e' `load` piu' `createDefinition`, e il\r\n * comando vive nel catalogo.\r\n */\r\n createDefinition(definition: DpeDefinition): Observable<DpeDefinitionSummary> {\r\n void definition;\r\n return missing('createDefinition');\r\n }\r\n\r\n /**\r\n * Cancellazione per nome. Opzionale: un catalogo di sola lettura e' un uso legittimo.\r\n *\r\n * Non esiste una primitiva di rinomina: rinominare cambia la chiave, quindi e'\r\n * creare-e-cancellare, cioe' una decisione di storage che e' dell'ospite (ADR 0019).\r\n */\r\n deleteDefinition(name: string): Observable<void> {\r\n void name;\r\n return missing('deleteDefinition');\r\n }\r\n}\r\n","/**\r\n * Implementazione HTTP di {@link DpeBuilderApi}, sulle rotte **normative** di\r\n * `docs/contracts.md` §7-bis.\r\n *\r\n * Vive nella libreria per decisione del committente (ADR 0020, che revisiona la sola clausola\r\n * dell'ADR 0007 sulla collocazione): l'integrazione e' due righe invece che un file da copiare.\r\n * Il prezzo, accettato e scritto: **le rotte sono contratto pubblico**, e cambiarne una e' un\r\n * breaking change. Da qui in poi questa classe non e' «un esempio» — una classe esportata da una\r\n * libreria viene usata in produzione a prescindere da come la chiamiamo nel commento, e chiamarla\r\n * esempio servirebbe solo a esentarla dai test.\r\n *\r\n * Resta vero cio' che conta dell'ADR 0007: **il contratto e' la classe astratta**, non questo\r\n * client. Nessun componente e nessuno store della libreria lo importa. Chi ha rotte diverse ha due\r\n * strade, e nessuna tocca l'editor:\r\n *\r\n * 1. **estendere questa classe** e sovrascrivere i soli metodi divergenti. I punti di estensione\r\n * sono `protected` di proposito: `url()` per il prefisso e la forma del percorso, `segment()`\r\n * per la codifica, i quattro verbi per il trasporto, `fail()` per la traduzione degli errori,\r\n * `request()` per cio' che avvolge ogni chiamata;\r\n * 2. **scrivere un'altra implementazione di `DpeBuilderApi` da zero** — legittima e supportata.\r\n *\r\n * Le tre cose che chi legge questo file deve sapere, e che nessun compilatore gli dira':\r\n *\r\n * 1. **il corpo e' il documento nudo** dove il backend analizza o archivia la definizione\r\n * (`validate`, `createDefinition`, `save`). L'unico involucro e' `compilePreview`, perche' li'\r\n * il corpo porta anche i valori dei parametri. Sbagliare involucro **non produce nessun\r\n * errore**: un backend che deserializza una `DpeDefinition` da `{definition: …}` non riconosce\r\n * nessuna proprieta', risponde `200`, e l'editor mostra la validazione di una definizione\r\n * vuota;\r\n * 2. **la serializzazione e' camelCase.** I tipi TypeScript sono allineati a mano ai record C#: un\r\n * backend che risponde in PascalCase non produce nessun errore, produce campi `undefined`;\r\n * 3. **l'errore di trasporto diventa una categoria.** L'editor si comporta in base alla categoria\r\n * e non legge mai il messaggio: `MissingService` disabilita un comando, `Transport` e'\r\n * ritentabile, `Backend` no.\r\n *\r\n * Cosa questo client **non** fa, e non per dimenticanza: nessun `retry`, nessun `shareReplay`,\r\n * nessuna cache, nessun debounce, nessuna intestazione propria. Traduce trasporto ed errori e\r\n * nient'altro — la cache vive negli store, il debounce nel chiamante, i tentativi e\r\n * l'autenticazione negli interceptor dell'ospite. E non tocca `definition.dialect` in nessun\r\n * metodo (ADR 0009 e 0017): sarebbe una migrazione silenziosa delle capacita' con cui il backend\r\n * compilera' la definizione.\r\n */\r\n\r\nimport { HttpClient, HttpErrorResponse } from '@angular/common/http';\r\nimport {\r\n EnvironmentProviders,\r\n InjectionToken,\r\n Injectable,\r\n inject,\r\n makeEnvironmentProviders,\r\n} from '@angular/core';\r\nimport { Observable, catchError, defer, map, throwError } from 'rxjs';\r\n\r\nimport { DpeBuilderApi } from './dpe-builder-api';\r\nimport { DpeApiError, DpeApiErrorCategory } from './dpe-api-error';\r\nimport { DpeDefinition } from '../model/dpe-definition.model';\r\nimport {\r\n DpeCompileResult,\r\n DpeDefinitionSummary,\r\n DpeEnvironment,\r\n DpeFormulaCatalogResult,\r\n DpeParameterValue,\r\n DpeValidateResult,\r\n TableLookupResult,\r\n TableSummary,\r\n} from '../model/dpe-api.model';\r\nimport type { DpeOptionalPrimitive } from '../core/dpe-capabilities';\r\n\r\n/**\r\n * Configurazione del client. Iniettata e non cablata: in una libreria una costante non e' una\r\n * scelta, e `const BASE = '/api/dpe'` in un file di libreria toglierebbe all'ospite la sua.\r\n */\r\nexport interface DpeBuilderHttpConfig {\r\n /** Prefisso comune degli endpoint, es. `/api/dpe` o `https://host/api/dpe`. Senza slash finale. */\r\n baseUrl?: string;\r\n /**\r\n * Primitive opzionali che questo ambiente **non serve** (`contracts.md` §7, dichiarazione\r\n * sottrattiva). E' l'unico modo che ha l'ospite di spegnere una primitiva che questo client\r\n * implementa per forza: conoscendo tutti e undici gli endpoint, li dichiarerebbe altrimenti tutti\r\n * disponibili, e un comando attivo su un endpoint non servito fallisce alla pressione.\r\n */\r\n unsupported?: readonly DpeOptionalPrimitive[];\r\n}\r\n\r\n/** Base di default delle rotte, in un solo posto: token e funzione di registrazione la condividono. */\r\nconst DPE_BUILDER_DEFAULT_BASE_URL = '/api/dpe';\r\n\r\nexport const DPE_BUILDER_HTTP_CONFIG = new InjectionToken<DpeBuilderHttpConfig>(\r\n 'DPE_BUILDER_HTTP_CONFIG',\r\n { factory: () => ({ baseUrl: DPE_BUILDER_DEFAULT_BASE_URL }) },\r\n);\r\n\r\n@Injectable()\r\nexport class HttpDpeBuilderApi extends DpeBuilderApi {\r\n protected readonly http = inject(HttpClient);\r\n protected readonly config = inject(DPE_BUILDER_HTTP_CONFIG);\r\n\r\n /**\r\n * Deriva da `DpeBuilderHttpConfig.unsupported`: e' il ponte fra la configurazione e la regola\r\n * di `contracts.md` §7. Non e' un doppione della configurazione — e' il campo che\r\n * `core/dpe-capabilities.ts` legge, e leggerlo dal token li' significherebbe portare la\r\n * configurazione HTTP dentro il calcolo delle capacita'.\r\n */\r\n override readonly unsupportedPrimitives: readonly DpeOptionalPrimitive[] =\r\n this.config.unsupported ?? [];\r\n\r\n // --- punti di estensione ---------------------------------------------------\r\n\r\n /**\r\n * Composizione dell'URL. Sovrascriverlo basta a spostare tutte e undici le rotte sotto un altro\r\n * prefisso senza toccare un metodo.\r\n *\r\n * `?? ''` e non un default: `baseUrl: ''` e' una configurazione **valida** e significa «rotte\r\n * relative alla radice», quindi non va rimpiazzata dal default del token. Il percorso porta\r\n * sempre lo slash iniziale, cosi' non nasce un doppio slash.\r\n */\r\n protected url(path: string): string {\r\n return `${this.config.baseUrl ?? ''}${path}`;\r\n }\r\n\r\n /** Codifica di un segmento variabile. Un nome di definizione puo' contenere `/`, spazi, `&`. */\r\n protected segment(value: string): string {\r\n return encodeURIComponent(value);\r\n }\r\n\r\n /**\r\n * La chiave che finisce in un segmento di rotta, **verificata prima di partire**.\r\n *\r\n * Perche' esiste: `encodeURIComponent(undefined)` vale `'undefined'`, e senza questo controllo un\r\n * nome mancante non produceva nessun errore — produceva `PUT /definitions/undefined`, cioe' una\r\n * richiesta sensata per il trasporto e priva di senso per il dominio. La sbaglia il backend\r\n * (`404`, o peggio: sovrascrive una definizione che si chiama davvero cosi'), e chi guarda la rete\r\n * vede una chiave inventata dal client senza sapere da dove arrivi.\r\n *\r\n * Rifiuta **senza emettere la richiesta**: una chiave assente non si ritenta e non riguarda il\r\n * backend. La categoria e' `Backend` e non `MissingService` — il comando c'e' e non va disabilitato\r\n * — ne' `Transport`, che significherebbe «ritenta».\r\n */\r\n protected requireKey(value: string | null | undefined, primitive: string, what: string): string {\r\n if (typeof value !== 'string' || value.trim().length === 0) {\r\n throw new DpeApiError(\r\n 'Backend',\r\n `${primitive}: ${what} manca, e finirebbe nell'URL come \"${String(value)}\". ` +\r\n 'Se il documento arriva dal backend, verifica che la risposta sia in camelCase: ' +\r\n '`Name` invece di `name` non produce nessun errore, produce un campo `undefined`.',\r\n );\r\n }\r\n return value;\r\n }\r\n\r\n /**\r\n * Cio' che avvolge **ogni** chiamata, e l'unico posto in cui si decide se una chiamata parte.\r\n *\r\n * `defer` non e' un vezzo: rende l'`Observable` **freddo** anche per il controllo delle\r\n * primitive non servite, cosi' chi non sottoscrive non valuta nulla e chi si disiscrive annulla\r\n * davvero la richiesta HTTP. E' la proprieta' per cui il contratto §7 e' `Observable` e non\r\n * `Promise` — l'editor la usa per non accumulare validazioni in volo.\r\n *\r\n * Il rigetto per primitiva non servita sta **fuori** dal `catchError`: passandogli dentro, un\r\n * `DpeApiError('MissingService')` verrebbe riclassificato `Transport` da `fail()`, che tratta\r\n * come guasto di rete tutto cio' che non e' una `HttpErrorResponse`.\r\n */\r\n protected request<T>(primitive: DpeOptionalPrimitive | string, call: () => Observable<T>): Observable<T> {\r\n return defer(() => {\r\n if (this.isUnsupported(primitive)) {\r\n return this.missing(primitive);\r\n }\r\n return call().pipe(catchError((error: unknown) => this.fail(error, primitive)));\r\n });\r\n }\r\n\r\n /** Vero se l'ospite ha dichiarato di non servire questa primitiva. */\r\n protected isUnsupported(primitive: string): boolean {\r\n return (this.unsupportedPrimitives as readonly string[]).includes(primitive);\r\n }\r\n\r\n /**\r\n * Rigetto di una primitiva dichiarata non servita, **senza toccare la rete**: sapere in anticipo\r\n * che un servizio non c'e' e chiederglielo comunque e' il difetto che `unsupportedPrimitives`\r\n * esiste per evitare, ed e' il motivo per cui il `501` -> `MissingService` non basta da solo.\r\n */\r\n protected missing(primitive: string): Observable<never> {\r\n return throwError(\r\n () =>\r\n new DpeApiError(\r\n 'MissingService',\r\n `Primitiva non servita da questo ambiente: ${primitive}.`,\r\n ),\r\n );\r\n }\r\n\r\n /**\r\n * `501 Not Implemented` diventa `MissingService`: e' cosi' che un ambiente incompleto degrada\r\n * **un comando** invece di rompere l'editor. Uno `0` (rete assente, CORS) e' `Transport`, come\r\n * qualunque errore che non sia una `HttpErrorResponse`; tutto il resto e' `Backend`.\r\n *\r\n * Il `404` resta deliberatamente `Backend` e non ha un ramo suo: qui non e' «primitiva assente»\r\n * ma «risorsa assente» — una definizione cancellata da altri, un nome che non esiste piu'. Chi\r\n * volesse distinguerlo lo mappi su una categoria propria, non su `MissingService`: disabilitare\r\n * un comando perche' un singolo nome non e' stato trovato sarebbe la reazione sbagliata.\r\n *\r\n * Il messaggio si prende in ordine — corpo dell'errore, messaggio della risposta, ripiego che\r\n * nomina la primitiva — e serve solo all'umano: l'editor guarda la categoria.\r\n */\r\n protected fail(error: unknown, primitive: string): Observable<never> {\r\n /*\r\n * Un `DpeApiError` passa **intatto**: l'ha classificato chi l'ha alzato (la chiave mancante, una\r\n * risposta che non e' una definizione), e ri-tradurlo qui lo farebbe cadere sul ramo «non e' una\r\n * `HttpErrorResponse`», cioe' `Transport` — che significa «ritenta» e sarebbe la reazione\r\n * sbagliata a un contratto rotto.\r\n */\r\n if (DpeApiError.is(error)) {\r\n return throwError(() => error);\r\n }\r\n const response = error instanceof HttpErrorResponse ? error : undefined;\r\n let category: DpeApiErrorCategory = 'Backend';\r\n if (response?.status === 501) {\r\n category = 'MissingService';\r\n } else if (!response || response.status === 0) {\r\n category = 'Transport';\r\n }\r\n const message =\r\n (response?.error as { message?: string } | undefined)?.message ??\r\n response?.message ??\r\n `Chiamata ${primitive} fallita.`;\r\n return throwError(() => new DpeApiError(category, message, error));\r\n }\r\n\r\n // --- verbi ----------------------------------------------------------------\r\n // Sovrascriverne uno cambia il trasporto di tutte le rotte che lo usano: e' il livello a cui\r\n // agisce chi deve aggiungere `withCredentials` o un `responseType` diverso.\r\n\r\n protected httpGet<T>(path: string): Observable<T> {\r\n return this.http.get<T>(this.url(path));\r\n }\r\n\r\n protected httpPost<T>(path: string, body: unknown): Observable<T> {\r\n return this.http.post<T>(this.url(path), body);\r\n }\r\n\r\n protected httpPut<T>(path: string, body: unknown): Observable<T> {\r\n return this.http.put<T>(this.url(path), body);\r\n }\r\n\r\n protected httpDelete<T>(path: string): Observable<T> {\r\n return this.http.delete<T>(this.url(path));\r\n }\r\n\r\n // --- primitive obbligatorie -----------------------------------------------\r\n\r\n /**\r\n * La rotta della validazione. Dal trasloco di `requiredForm` i suoi `schemas` portano **tre**\r\n * fatti per nodo e non due — colonne, compilabilita' e forma richiesta dai siti di espressione —\r\n * e un ospite che serva questa rotta a mano deve valorizzare anche il terzo: e' l'unica strada\r\n * per cui la forma arriva all'editor, e senza di essa un campo formula non dice cosa vuole ne'\r\n * si blocca quando il compilatore non sa produrla.\r\n */\r\n override validate(definition: DpeDefinition): Observable<DpeValidateResult> {\r\n // Il documento va **nudo** nel body: e' la definizione in lavorazione, non un involucro.\r\n return this.request('validate', () =>\r\n this.httpPost<DpeValidateResult>('/validate', definition),\r\n );\r\n }\r\n\r\n override getTables(): Observable<TableSummary[]> {\r\n return this.request('getTables', () => this.httpGet<TableSummary[]>('/tables'));\r\n }\r\n\r\n override getTable(tableName: string): Observable<TableLookupResult> {\r\n return this.request('getTable', () =>\r\n this.httpGet<TableLookupResult>(\r\n `/tables/${this.segment(this.requireKey(tableName, 'getTable', 'il nome della tabella'))}`,\r\n ),\r\n );\r\n }\r\n\r\n // --- primitive opzionali --------------------------------------------------\r\n\r\n /** L'**unica** rotta con involucro: il corpo porta anche i valori dei parametri (§7-bis). */\r\n override compilePreview(\r\n definition: DpeDefinition,\r\n parameterValues?: DpeParameterValue[],\r\n ): Observable<DpeCompileResult> {\r\n return this.request('compilePreview', () =>\r\n this.httpPost<DpeCompileResult>('/compile', { definition, parameterValues }),\r\n );\r\n }\r\n\r\n override load(name: string): Observable<DpeDefinition> {\r\n return this.request('load', () =>\r\n this.httpGet<DpeDefinition>(\r\n `/definitions/${this.segment(this.requireKey(name, 'load', 'il nome della definizione'))}`,\r\n ).pipe(map((payload) => this.requireDefinition(payload))),\r\n );\r\n }\r\n\r\n /**\r\n * La risposta di `load` **e' una definizione**, e qui si verifica invece di sperarlo.\r\n *\r\n * Costa quattro righe e trasforma il difetto peggiore di questo contratto in un errore immediato\r\n * che nomina la causa. Senza, una risposta della forma sbagliata non produce nessun errore:\r\n * produce un editor con il canvas vuoto e un `name` `undefined`, che si manifesta molto piu' tardi\r\n * come un salvataggio verso una chiave inventata. I tre modi in cui succede davvero:\r\n *\r\n * - il backend restituisce **l'involucro della lettura** (`{ definition, diagnostics, hasErrors }`)\r\n * invece del documento: e' l'esito di `IDpeService.Deserialize`, ed e' un errore facile da fare\r\n * scrivendo `Ok(read)` al posto di `Ok(documento)`;\r\n * - la risposta e' in **PascalCase**: `Name` non e' `name`, e in TypeScript non e' un errore;\r\n * - la rotta risponde un **sommario** (`DpeDefinitionSummary`) invece della definizione: si\r\n * somigliano, e il sommario ha `name` ma non `formatVersion` ne' i nodi.\r\n *\r\n * Si controllano le due proprieta' **obbligatorie** del documento e nient'altro: una definizione\r\n * puo' legittimamente non avere nessun nodo, e verificare di piu' vorrebbe dire validare, che e'\r\n * del backend.\r\n */\r\n protected requireDefinition(payload: unknown): DpeDefinition {\r\n const documento = payload as Partial<DpeDefinition> | null | undefined;\r\n const haNome = typeof documento?.name === 'string' && documento.name.trim().length > 0;\r\n const haVersione = typeof documento?.formatVersion === 'number';\r\n if (!haNome || !haVersione) {\r\n throw new DpeApiError(\r\n 'Backend',\r\n \"load: la risposta non e' una definizione (mancano `name` e/o `formatVersion`). \" +\r\n \"Le tre cause reali: il backend restituisce l'involucro di lettura \" +\r\n \"(`{ definition, diagnostics }`) invece del documento; risponde in PascalCase \" +\r\n \"(`Name` non e' `name`); risponde un sommario invece della definizione.\",\r\n payload,\r\n );\r\n }\r\n return payload as DpeDefinition;\r\n }\r\n\r\n /**\r\n * Il nome viene dal **documento** e non da un parametro: e' la chiave, e la rotta e'\r\n * `PUT /definitions/{definition.name}`. Se manca, la chiamata si rifiuta qui — un salvataggio\r\n * senza chiave non e' un salvataggio, e una definizione nuova si crea con `createDefinition`.\r\n */\r\n override save(definition: DpeDefinition): Observable<void> {\r\n return this.request('save', () =>\r\n this.httpPut<void>(\r\n `/definitions/${this.segment(this.requireKey(definition?.name, 'save', 'il nome della definizione'))}`,\r\n definition,\r\n ),\r\n );\r\n }\r\n\r\n /**\r\n * La dichiarazione dell'ambiente (ADR 0017). Questo client la implementa perche' conosce\r\n * l'endpoint, non perche' sappia se l'ospite lo serve — ed e' precisamente la ragione per cui\r\n * l'ADR 0020 ha aggiunto `unsupportedPrimitives`.\r\n *\r\n * Non e' un dettaglio: l'abilitazione della creazione nel catalogo (ADR 0019) dipende da questa\r\n * primitiva, quindi un ambiente che non la serve deve lasciare il comando disabilitato **con il\r\n * motivo**. Da qui i **tre** rami, che sono tre cose diverse e vanno tenute distinte:\r\n *\r\n * - **primitiva non implementata**: il metodo non viene sovrascritto, e il `MissingService` lo\r\n * produce l'identita' stessa del metodo. E' la strada di chi scrive la propria\r\n * implementazione di `DpeBuilderApi`, non di chi eredita questo client;\r\n * - **primitiva implementata ma non servita da questo ambiente**: e' il ramo centrale, quello\r\n * che questo file rende necessario. L'ospite mette `'getEnvironment'` in\r\n * `DpeBuilderHttpConfig.unsupported`, il comando resta disabilitato con il motivo, e **nessuna\r\n * richiesta parte**. Un metodo ereditato non si puo' dis-sovrascrivere: senza questa\r\n * dichiarazione il comando risulterebbe attivo;\r\n * - **primitiva servita che fallisce**: l'endpoint risponde male, e l'errore e' `Transport` o\r\n * `Backend`. Un ambiente rotto, non un ambiente incompleto.\r\n *\r\n * Rispondere `501` da questo endpoint per significare «non lo dichiaro» arriva allo stesso posto\r\n * nel modo sbagliato: costa un giro di rete a ogni apertura dell'editor per comunicare un fatto\r\n * che si conosce a tempo di configurazione.\r\n *\r\n * Cio' che l'editor fa della risposta resta suo: mostra il dialetto in sola lettura e segnala\r\n * senza correggere se differisce da quello della definizione.\r\n */\r\n override getEnvironment(): Observable<DpeEnvironment> {\r\n return this.request('getEnvironment', () => this.httpGet<DpeEnvironment>('/environment'));\r\n }\r\n\r\n /**\r\n * L'**undicesima rotta** (ADR 0036): `POST {base}/formula-catalog/{nodeName}`, corpo il documento\r\n * nudo. Le altre dieci non cambiano.\r\n *\r\n * Perche' un `POST` e non un `GET`: il catalogo dipende dallo **schema a monte** del nodo, che il\r\n * backend sa risolvere solo avendo la definizione, e la definizione aperta nell'editor puo' non\r\n * essere ancora salvata. Un `GET` con il solo nome del nodo risponderebbe sul documento\r\n * archiviato, cioe' su un altro documento.\r\n *\r\n * Perche' il nome del nodo sta nel **percorso** e non nel corpo: un involucro\r\n * `{ definition, nodeName }` e' esattamente l'errore silenzioso del contratto — un backend che\r\n * deserializza una `DpeDefinition` da `{definition: …}` non riconosce nessuna proprieta',\r\n * risponde `200`, e l'editor riceve il catalogo di una definizione vuota. Meglio una rotta che\r\n * non puo' sbagliare che un secondo corpo con involucro.\r\n *\r\n * **La risposta e' `DpeFormulaCatalogResult`, non `DpeFormulaCatalog`** (`contracts.md` §7-bis).\r\n * E' l'unica correzione di questa rotta: consegnava il catalogo nudo, quindi i tre esiti del\r\n * service ne diventavano due e un dialetto non coperto si presentava come un guasto di rete. Il\r\n * client non interpreta niente di cio' che riceve — la tricotomia si legge in\r\n * `dpeFormulaCatalogOutcome`, dove e' pura e verificabile.\r\n *\r\n * **Rottura dichiarata del contratto di rotta:** la risposta non porta piu' `requiredForm`. La\r\n * forma richiesta dai siti di espressione di un nodo sta ora su `DpeNodeSchemaInfo.requiredForm`,\r\n * cioe' negli schemi di `POST {base}/validate`. Un ospite che la serializzi ancora qui non rompe\r\n * niente — il client passa il corpo intero e nessuno la legge — ma resta l'unica copia di un\r\n * fatto che l'editor prende da un'altra parte. Il motivo del trasloco e' che questa rotta\r\n * l'editor non la chiama affatto quando `DpeEnvironment.providesFormulaCatalog` e' falso: la\r\n * forma non arrivava mai, proprio dagli ospiti piu' laconici.\r\n */\r\n override getFormulaCatalog(\r\n definition: DpeDefinition,\r\n nodeName: string,\r\n ): Observable<DpeFormulaCatalogResult> {\r\n return this.request('getFormulaCatalog', () =>\r\n this.httpPost<DpeFormulaCatalogResult>(\r\n `/formula-catalog/${this.segment(\r\n this.requireKey(nodeName, 'getFormulaCatalog', 'il nome del nodo'),\r\n )}`,\r\n definition,\r\n ),\r\n );\r\n }\r\n\r\n // --- catalogo (ADR 0019) --------------------------------------------------\r\n\r\n /** Nessun parametro di interrogazione: il filtro del catalogo e' locale, di proposito. */\r\n override listDefinitions(): Observable<DpeDefinitionSummary[]> {\r\n return this.request('listDefinitions', () =>\r\n this.httpGet<DpeDefinitionSummary[]>('/definitions'),\r\n );\r\n }\r\n\r\n /**\r\n * `POST` e non `PUT`: e' una **creazione**, quindi il backend puo' rifiutare un nome duplicato —\r\n * ed e' l'unica ragione per cui questa primitiva esiste separata da `save`. La risposta e' il\r\n * **sommario**, che chi crea deve adottare: il nome puo' essere stato normalizzato.\r\n */\r\n override createDefinition(definition: DpeDefinition): Observable<DpeDefinitionSummary> {\r\n return this.request('createDefinition', () =>\r\n this.httpPost<DpeDefinitionSummary>('/definitions', definition),\r\n );\r\n }\r\n\r\n override deleteDefinition(name: string): Observable<void> {\r\n return this.request('deleteDefinition', () =>\r\n this.httpDelete<void>(\r\n `/definitions/${this.segment(this.requireKey(name, 'deleteDefinition', 'il nome della definizione'))}`,\r\n ),\r\n );\r\n }\r\n}\r\n\r\n/**\r\n * Registra il client e la sua configurazione: l'integrazione in una riga che l'ADR 0020 chiedeva.\r\n *\r\n * **Non** chiama `provideHttpClient()`, e non e' una dimenticanza: il backend (`withFetch`), gli\r\n * interceptor, l'autenticazione e i cookie sono scelte dell'applicazione ospite, e una libreria\r\n * che le prende al posto suo la costringe a disfarle.\r\n *\r\n * `baseUrl` omesso ricade sul default, `baseUrl: ''` **no**: la stringa vuota e' una\r\n * configurazione valida («rotte relative alla radice») e un `||` la scambierebbe per assente.\r\n */\r\nexport function provideDpeBuilderHttpApi(config?: DpeBuilderHttpConfig): EnvironmentProviders {\r\n return makeEnvironmentProviders([\r\n {\r\n provide: DPE_BUILDER_HTTP_CONFIG,\r\n useValue: {\r\n baseUrl: config?.baseUrl ?? DPE_BUILDER_DEFAULT_BASE_URL,\r\n unsupported: config?.unsupported ?? [],\r\n } satisfies DpeBuilderHttpConfig,\r\n },\r\n { provide: DpeBuilderApi, useClass: HttpDpeBuilderApi },\r\n ]);\r\n}\r\n","/**\r\n * Modello della definizione DPE — traduzione **letterale** dei record C# di\r\n * `docs/domain-model.md`.\r\n *\r\n * Questi tipi sono allineati **a mano** ai record di `Jace.DPE.Model` (ADR 0007: nessun\r\n * generatore di tipi, nessuno JSON Schema). Sono due modelli in due linguaggi e il\r\n * compilatore non dira' mai quando divergono: la fonte di verita' e' `docs/contracts.md` +\r\n * `docs/domain-model.md`, e chi cambia un record C# aggiorna questo file nello stesso\r\n * momento.\r\n *\r\n * Convenzioni, tutte conseguenza del contratto:\r\n *\r\n * - proprieta' in **camelCase**, perche' il JSON prodotto dal backend e' camelCase\r\n * (`domain-model.md`, convenzioni generali);\r\n * - gli enum C# diventano **unioni di stringhe letterali** con i nomi esatti dei valori: un\r\n * `enum` TypeScript non e' assegnabile da una stringa arrivata dalla rete, e un valore\r\n * numerico non e' cio' che il backend serializza;\r\n * - le collezioni nel backend non sono mai nulle, ma il JSON le **omette quando vuote**\r\n * (ADR 0003): qui sono opzionali e chi legge non presume che esistano;\r\n * - `extensionData` esiste su **ogni** record (**I7**): sono proprieta' JSON non riconosciute,\r\n * con valori **opachi**. L'editor le trasporta senza interpretarle.\r\n *\r\n * Nessuna regola di dominio vive qui: obbligatorieta' condizionali, unicita' dei nomi e\r\n * compatibilita' dei tipi sono del backend. Le note `condiz.` nei commenti sono descrittive\r\n * e servono a chi costruira' gli ispettori, non sono validazione.\r\n */\r\n\r\n/** Proprieta' JSON non riconosciute, conservate verbatim (**I7**). Valori opachi. */\r\nexport type DpeExtensionData = Record<string, unknown>;\r\n\r\n/** Base di ogni record del modello: `ExtensionData` sta su tutti, non solo sulla radice. */\r\nexport interface DpeExtensible {\r\n extensionData?: DpeExtensionData;\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// 1. Elementi trasversali\r\n// ---------------------------------------------------------------------------\r\n\r\n/** I sei tipi logici, nessun altro valore mai (ADR 0002). */\r\nexport type LogicalType = 'String' | 'Number' | 'Integer' | 'DateTime' | 'Boolean' | 'Enum';\r\n\r\nexport interface ColumnType extends DpeExtensible {\r\n kind: LogicalType;\r\n /** Condiz.: richiesto per `String` nei campi dichiarati. */\r\n length?: number;\r\n /** Solo `Number`. */\r\n precision?: number;\r\n /** Condiz.: richiesto per `Number` nei campi dichiarati. */\r\n scale?: number;\r\n /** Condiz.: non vuoto quando `kind === 'Enum'`. */\r\n allowedValues?: string[];\r\n}\r\n\r\nexport type SortDirection = 'Ascending' | 'Descending';\r\n\r\nexport interface OrderByField extends DpeExtensible {\r\n /** Alias della colonna a monte. */\r\n columnName: string;\r\n /** Default `Ascending`. */\r\n direction?: SortDirection;\r\n}\r\n\r\nexport type AggregateFunction =\r\n | 'Count'\r\n | 'CountDistinct'\r\n | 'Sum'\r\n | 'Min'\r\n | 'Max'\r\n | 'Avg'\r\n | 'StdDev'\r\n | 'StdDevPop'\r\n | 'Variance'\r\n | 'VariancePop';\r\n\r\n/** Base di tutti i nodi. `name` e' univoco nell'intera definizione (**I1**). */\r\nexport interface DpeNode extends DpeExtensible {\r\n name: string;\r\n label: string;\r\n description?: string;\r\n}\r\n\r\n/**\r\n * I riferimenti a un nodo a monte sono dichiarati **opzionali** anche dove\r\n * `domain-model.md` li marca obbligatori, e non e' un ammorbidimento del contratto: e' cio'\r\n * che il contratto dice. In C# sono `string` con default `null`, l'obbligatorieta' e'\r\n * verificata dalla validazione e \"un modello incompleto e' un fatto normale (l'editor ne\r\n * produce uno a ogni tasto)\". Un nodo appena creato o appena scollegato **non ha** una\r\n * sorgente: rappresentarla con la stringa vuota sarebbe un valore magico che il backend\r\n * legge diversamente da un'assenza.\r\n */\r\n\r\n\r\n// ---------------------------------------------------------------------------\r\n// 2. Radice\r\n// ---------------------------------------------------------------------------\r\n\r\nexport type SqlDialectKind = 'Sqlite' | 'SqlServer' | 'PostgreSql' | 'Oracle';\r\nexport type DefinitionStatus = 'Active' | 'Inactive';\r\nexport type DefinitionRunMode = 'Batch' | 'OnDemand';\r\n\r\nexport interface DpeDefinition extends DpeExtensible {\r\n /** Default `1`, sempre serializzato per primo. */\r\n formatVersion: number;\r\n name: string;\r\n label: string;\r\n description?: string;\r\n /** Il dialetto e' **parte della definizione** (ADR 0009): cambiarlo e' una migrazione. */\r\n dialect: SqlDialectKind;\r\n /** Default `Inactive`. */\r\n status?: DefinitionStatus;\r\n /** Default `Batch`. */\r\n runMode?: DefinitionRunMode;\r\n /** Default `false`. */\r\n isTemplate?: boolean;\r\n parameters?: DpeParameter[];\r\n /** Obbligatoria (>=1) in una definizione valida; assente in una in lavorazione. */\r\n dataSources?: DataSourceNode[];\r\n filters?: FilterNode[];\r\n joins?: JoinNode[];\r\n aggregates?: AggregateNode[];\r\n transforms?: TransformNode[];\r\n appends?: AppendNode[];\r\n hierarchyPaths?: HierarchyPathNode[];\r\n /** Modellati, non compilabili in v1 (`DPE_NODE_NOT_COMPILABLE`). */\r\n forecasts?: ForecastNode[];\r\n /** Modellati, non compilabili in v1. */\r\n customNodes?: CustomNode[];\r\n /** Obbligatoria (>=1) in una definizione valida. */\r\n writebacks?: WritebackNode[];\r\n atomicWritebacks?: AtomicWritebackNode[];\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// 3. Parametri\r\n// ---------------------------------------------------------------------------\r\n\r\nexport type ParameterRole = 'Value' | 'Filter' | 'Expression';\r\n\r\nexport interface DpeParameter extends DpeExtensible {\r\n name: string;\r\n label: string;\r\n description?: string;\r\n role: ParameterRole;\r\n /** Condiz.: richiesto quando `role === 'Value'`. */\r\n valueType?: ColumnType;\r\n /** Testo, interpretato secondo `valueType`, cultura invariante. */\r\n defaultValue?: string;\r\n /** Default `false`; ammesso solo per `role === 'Value'`. */\r\n isMultiValue?: boolean;\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// 4. Nodi sorgente\r\n// ---------------------------------------------------------------------------\r\n\r\nexport type DataSourceKind = 'Table' | 'Csv' | 'ExternalDataset';\r\nexport type CsvDelimiter = 'Comma' | 'Semicolon' | 'Tab' | 'Pipe' | 'Caret' | 'Backquote';\r\n\r\nexport interface CsvSourceOptions extends DpeExtensible {\r\n /** Default `Comma`. */\r\n delimiter?: CsvDelimiter;\r\n /** Default `true`. */\r\n hasHeaderRow?: boolean;\r\n /** Identificatore opaco risolto dall'host. */\r\n location: string;\r\n}\r\n\r\nexport interface DataSourceField extends DpeExtensible {\r\n /** Nome del campo nella tabella/file di origine. */\r\n name: string;\r\n /** Quando assente l'alias **e'** `name`: la normalizzazione e' del backend. */\r\n alias?: string;\r\n /** Condiz.: richiesto per `Csv`/`ExternalDataset`; per `Table` **ignorato**. */\r\n declaredType?: ColumnType;\r\n /** Default `false`. */\r\n isPrimaryKey?: boolean;\r\n}\r\n\r\nexport interface DataSourceNode extends DpeNode {\r\n kind: DataSourceKind;\r\n /** Condiz.: richiesto per `kind === 'Table'`; nome noto al provider di metadati. */\r\n tableName?: string;\r\n /** Obbligatorio (>=1) in un nodo valido. */\r\n fields?: DataSourceField[];\r\n /** Condiz.: richiesto per `kind === 'Csv'`. */\r\n csvOptions?: CsvSourceOptions;\r\n /** Condiz.: richiesto per `kind === 'ExternalDataset'`. */\r\n externalDatasetName?: string;\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// 5. Nodi di trasformazione\r\n// ---------------------------------------------------------------------------\r\n\r\nexport type FilterOperator =\r\n | 'Equals'\r\n | 'NotEquals'\r\n | 'GreaterThan'\r\n | 'GreaterThanOrEqual'\r\n | 'LessThan'\r\n | 'LessThanOrEqual'\r\n | 'StartsWith'\r\n | 'EndsWith'\r\n | 'Contains'\r\n | 'DoesNotContain'\r\n | 'IsNull'\r\n | 'IsNotNull'\r\n | 'In'\r\n | 'NotIn';\r\n\r\nexport interface FilterCriterion extends DpeExtensible {\r\n /** >=1, univoco nel nodo, referenziato da `condition`. */\r\n sequence: number;\r\n /** Alias a monte. */\r\n sourceFieldName: string;\r\n operator: FilterOperator;\r\n /** Condiz.: richiesto salvo `IsNull`/`IsNotNull` e salvo `inputVariable` presente. */\r\n value?: string;\r\n /** Nome di un `DpeParameter` con `role === 'Value'`; esclusivo con `value`. */\r\n inputVariable?: string;\r\n}\r\n\r\nexport interface FilterNode extends DpeNode {\r\n sourceName?: string;\r\n /** Default `false`. */\r\n isDynamic?: boolean;\r\n /** Condiz.: richiesto (>=1) quando `isDynamic` e' falso. */\r\n criteria?: FilterCriterion[];\r\n /**\r\n * Condiz.: richiesto quando `isDynamic` e' falso. Combinazione booleana dei `sequence`\r\n * dei criteri (`1 AND (2 OR 3)`), **non** un'espressione SQL: e' l'unico linguaggio che il\r\n * backend parsifica davvero.\r\n */\r\n condition?: string;\r\n /** Condiz.: richiesto quando `isDynamic`; riferisce un parametro con `role === 'Filter'`. */\r\n filterParameterName?: string;\r\n}\r\n\r\nexport type JoinKind = 'Inner' | 'LeftOuter' | 'RightOuter' | 'FullOuter' | 'Lookup';\r\n\r\nexport interface JoinKey extends DpeExtensible {\r\n primaryFieldName: string;\r\n secondaryFieldName: string;\r\n}\r\n\r\nexport interface JoinResultField extends DpeExtensible {\r\n /** Deve essere `primarySourceName` o `secondarySourceName` del nodo. */\r\n sourceName: string;\r\n sourceFieldName: string;\r\n /** Univoco nel nodo (**I1**). */\r\n alias: string;\r\n}\r\n\r\nexport interface JoinNode extends DpeNode {\r\n primarySourceName?: string;\r\n secondarySourceName?: string;\r\n kind: JoinKind;\r\n /** Obbligatorio (>=1) in un nodo valido. */\r\n keys?: JoinKey[];\r\n /** Lo schema di output e' **esplicito**: solo questi campi. */\r\n fields?: JoinResultField[];\r\n /** Default `false`; significativo solo per `kind === 'Lookup'`. */\r\n allowsMultipleLookupValues?: boolean;\r\n}\r\n\r\nexport interface AggregateField extends DpeExtensible {\r\n /** Alias a monte; assente **solo** per `Count` (conteggio righe). */\r\n sourceFieldName?: string;\r\n function: AggregateFunction;\r\n alias: string;\r\n}\r\n\r\nexport interface AggregateNode extends DpeNode {\r\n sourceName?: string;\r\n /** Alias a monte; vuoto = aggregazione globale su una riga. */\r\n groupBy?: string[];\r\n /** Obbligatorio (>=1) in un nodo valido. */\r\n fields?: AggregateField[];\r\n}\r\n\r\nexport type TransformKind = 'Expression' | 'Slice' | 'ComputeRelative';\r\n\r\nexport interface ExpressionField extends DpeExtensible {\r\n alias: string;\r\n /**\r\n * Una **formula** nel linguaggio del compilatore registrato dall'ospite (ADR 0031): non e'\r\n * SQL, e il frontend non ne conosce la grammatica. Forma `Value` sotto `Expression`, forma\r\n * `WindowFunction` sotto `ComputeRelative`.\r\n */\r\n expression: string;\r\n /**\r\n * **Dichiarato dall'utente e autorevole** (ADR 0033): lo schema resta una funzione pura della\r\n * definizione, quindi il tipo di una colonna calcolata e' questo e non quello inferito dal\r\n * compilatore. Il compilatore lo **verifica** — `DPE_EXPR_TYPE_MISMATCH` /\r\n * `DPE_EXPR_TYPE_WIDENED` / `DPE_EXPR_TYPE_UNVERIFIED` — ma non lo determina.\r\n */\r\n declaredType: ColumnType;\r\n}\r\n\r\nexport interface TransformNode extends DpeNode {\r\n sourceName?: string;\r\n kind: TransformKind;\r\n /** Condiz.: richiesto (>=1) per `Expression`/`ComputeRelative`, vuoto per `Slice`. */\r\n expressionFields?: ExpressionField[];\r\n /** Condiz.: richiesto (>=1) per `Slice`, vuoto altrimenti. */\r\n droppedFields?: string[];\r\n /** Solo `ComputeRelative`. */\r\n partitionBy?: string[];\r\n /** Solo `ComputeRelative`. */\r\n orderBy?: OrderByField[];\r\n}\r\n\r\nexport interface AppendNode extends DpeNode {\r\n /** Obbligatorio (>=2) in un nodo valido. */\r\n sources?: string[];\r\n /** Default `false`. */\r\n allowsDisjointSchema?: boolean;\r\n}\r\n\r\nexport interface HierarchyPathNode extends DpeNode {\r\n sourceName?: string;\r\n /** Chiave del nodo corrente. */\r\n selfFieldName?: string;\r\n /** Riferimento al padre. */\r\n parentFieldName?: string;\r\n /** Alias della colonna di percorso prodotta (`String`). */\r\n pathFieldName?: string;\r\n /** Default `true`. */\r\n includesSelfValue?: boolean;\r\n /** Condiz.: richiesto (>=1) quando `isAggregationRequired`. */\r\n aggregateFields?: AggregateField[];\r\n /** Default `false`. */\r\n isAggregationRequired?: boolean;\r\n rollupPercentageFieldName?: string;\r\n}\r\n\r\nexport type ForecastPeriodType =\r\n | 'Year'\r\n | 'YearQuarter'\r\n | 'YearMonth'\r\n | 'YearWeek'\r\n | 'YearMonthDay'\r\n | 'FiscalYear'\r\n | 'FiscalYearQuarter'\r\n | 'FiscalYearMonth'\r\n | 'FiscalYearWeek';\r\n\r\nexport type ForecastModelType = 'Auto' | 'Additive' | 'Multiplicative';\r\nexport type ForecastAccuracy = 'None' | 'Eighty' | 'NinetyFive';\r\n\r\nexport type DatePart =\r\n | 'Second'\r\n | 'Minute'\r\n | 'Hour'\r\n | 'Day'\r\n | 'Week'\r\n | 'Month'\r\n | 'Quarter'\r\n | 'Year';\r\n\r\nexport interface ForecastAggregationField extends DpeExtensible {\r\n fieldName: string;\r\n function: AggregateFunction;\r\n resultAlias: string;\r\n}\r\n\r\nexport interface ForecastGroupField extends DpeExtensible {\r\n fieldName: string;\r\n /** Condiz.: richiesto quando il campo e' `DateTime`. */\r\n datePart?: DatePart;\r\n}\r\n\r\n/** Modellato per intero, **non compilabile in v1** (`DPE_NODE_NOT_COMPILABLE`). */\r\nexport interface ForecastNode extends DpeNode {\r\n sourceName?: string;\r\n dateFieldName?: string;\r\n periodType?: ForecastPeriodType;\r\n /** Default `1`, ammesso 1..100. */\r\n periodCount?: number;\r\n /** Alias della colonna di inizio periodo prodotta. */\r\n periodStartFieldName?: string;\r\n /** Default `Auto`. */\r\n modelType?: ForecastModelType;\r\n /** `null`/assente = automatica; ammesso 2..24. */\r\n seasonality?: number;\r\n /** Default `None`. */\r\n accuracyPercent?: ForecastAccuracy;\r\n /** Default `false`. */\r\n excludesLastPeriod?: boolean;\r\n /** Obbligatorio (>=1) in un nodo valido. */\r\n aggregationFields?: ForecastAggregationField[];\r\n groupFields?: ForecastGroupField[];\r\n}\r\n\r\n/** Usato dove uno schema si **dichiara** invece di derivarlo. */\r\nexport interface DeclaredField extends DpeExtensible {\r\n name: string;\r\n type: ColumnType;\r\n}\r\n\r\nexport interface CustomNodeParameter extends DpeExtensible {\r\n name: string;\r\n value: string;\r\n}\r\n\r\n/** Modellato per intero, **non compilabile in v1**. */\r\nexport interface CustomNode extends DpeNode {\r\n /** Obbligatorio (>=1) in un nodo valido. */\r\n sources?: string[];\r\n extensionName: string;\r\n extensionNamespace?: string;\r\n parameters?: CustomNodeParameter[];\r\n /**\r\n * Obbligatorio (>=1): senza schema dichiarato la propagazione (**I4**) si interrompe e\r\n * tutto cio' che sta a valle diventa invalidabile.\r\n */\r\n outputFields?: DeclaredField[];\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// 6. Nodi sink\r\n// ---------------------------------------------------------------------------\r\n\r\nexport type WritebackTargetKind = 'Database' | 'Json' | 'Csv';\r\nexport type WritebackOperation = 'Insert' | 'Update' | 'Upsert' | 'Delete';\r\nexport type WritebackFieldRole = 'Value' | 'PrimaryKey' | 'QualifierKey';\r\n\r\n/**\r\n * Granularita' del fallimento che l'host deve rispettare applicando un writeback (ADR 0040).\r\n *\r\n * `PerRecord`: l'host **puo'** applicare le righe indipendentemente e riportare i fallimenti per\r\n * riga. `Atomic`: l'host **deve** applicare l'intero writeback in una transazione sola.\r\n *\r\n * Dichiarata e **non eseguita**: la libreria non apre transazioni e questo valore non cambia di\r\n * una virgola l'SQL generato.\r\n */\r\nexport type WritebackFailureHandling = 'Atomic' | 'PerRecord';\r\n\r\nexport interface WritebackFieldMapping extends DpeExtensible {\r\n /** Condiz.: alias a monte; assente quando `runtimeParameterName` e' presente. */\r\n sourceFieldName?: string;\r\n targetFieldName: string;\r\n /** Il valore viene da un parametro, non dalla riga; esclusivo con `sourceFieldName`. */\r\n runtimeParameterName?: string;\r\n /** Default `Value`. */\r\n role?: WritebackFieldRole;\r\n relationshipName?: string;\r\n /** Condiz.: richiesto quando `relationshipName` e' presente. */\r\n parentName?: string;\r\n /** Default `false`; se `true`, `sourceFieldName` non e' richiesto. */\r\n isAutoGenerated?: boolean;\r\n}\r\n\r\n/**\r\n * Opzioni dell'artefatto JSON (`contracts.md` §2, «Opzioni degli artefatti»). Sono **solo**\r\n * scelte di formato, mai di destinazione (ADR 0010).\r\n */\r\nexport interface JsonArtifactOptions extends DpeExtensible {\r\n /** Default `false`: JSON compatto, array di oggetti. */\r\n indented?: boolean;\r\n}\r\n\r\n/**\r\n * Opzioni dell'artefatto CSV. Il quoting **non e' configurabile** di proposito: si quota\r\n * quando serve e si raddoppiano le virgolette, perche' un quoting opzionale produce file che\r\n * qualcuno non riesce a rileggere.\r\n */\r\nexport interface CsvArtifactOptions extends DpeExtensible {\r\n /** Default `Comma`. */\r\n delimiter?: CsvDelimiter;\r\n /** Default `true`. */\r\n includesHeaderRow?: boolean;\r\n}\r\n\r\nexport interface WritebackNode extends DpeNode {\r\n sourceName?: string;\r\n /** **Determina l'artefatto prodotto**. */\r\n target: WritebackTargetKind;\r\n /** >=1, univoco fra tutti i writeback e gli atomic writeback. */\r\n sequence: number;\r\n /** Condiz.: richiesto per `target === 'Database'`. */\r\n tableName?: string;\r\n /** Condiz.: richiesto per `target === 'Database'`; assente per `Json`/`Csv`. */\r\n operation?: WritebackOperation;\r\n /** Obbligatorio (>=1) in un nodo valido. */\r\n fields?: WritebackFieldMapping[];\r\n /**\r\n * Predicato applicato al risultato: e' il **terzo sito di authoring di formule** (ADR 0031 §1),\r\n * quello che si dimentica, e passa dal compilatore nella forma `Predicate`. Non e' SQL.\r\n */\r\n filterCondition?: string;\r\n /** Condiz.: richiesto per `operation === 'Upsert'`. */\r\n externalIdFieldName?: string;\r\n /** Default `false`. */\r\n onlyChangedRows?: boolean;\r\n /**\r\n * `targetFieldName` della colonna con cui l'host raggruppa le righe per la rilavorazione\r\n * (ADR 0040). Solo `target === 'Database'`. Dichiarato e non eseguito: la libreria non\r\n * raggruppa niente, e senza questo campo l'unico lotto di lavoro possibile e' il writeback\r\n * intero.\r\n */\r\n processingGroupFieldName?: string;\r\n /**\r\n * Solo `target === 'Database'`, e **assente** vuol dire «non dichiarato», che non e' la stessa\r\n * cosa di `PerRecord`: il valore efficace lo calcola il backend e arriva su\r\n * `DpeWritebackStep.failureHandling`.\r\n *\r\n * L'opzionalita' non e' comodita': e' cio' che permette a un salva-dopo-carica di rispedire il\r\n * documento **identico** (**I7**). Se l'editor scrivesse un default qui, una definizione che non\r\n * dichiara niente tornerebbe al backend dichiarando `PerRecord`, e su un membro di gruppo\r\n * atomico quella riga in piu' diventerebbe `DPE_MODEL_CONDITIONAL_FORBIDDEN` su un campo che\r\n * nessuno ha toccato.\r\n */\r\n failureHandling?: WritebackFailureHandling;\r\n /** Solo `target === 'Json'`. */\r\n jsonOptions?: JsonArtifactOptions;\r\n /** Solo `target === 'Csv'`. */\r\n csvOptions?: CsvArtifactOptions;\r\n}\r\n\r\nexport interface AtomicWritebackRelationship extends DpeExtensible {\r\n /** Nome di un `WritebackNode` con `target === 'Database'`. */\r\n parentWritebackName?: string;\r\n parentFieldName?: string;\r\n childWritebackName?: string;\r\n childFieldName?: string;\r\n relationshipName?: string;\r\n /** Ordine dentro il gruppo atomico. */\r\n sequence: number;\r\n}\r\n\r\nexport interface AtomicWritebackNode extends DpeNode {\r\n /** Nello stesso spazio di numerazione dei `WritebackNode`. */\r\n sequence: number;\r\n /** Obbligatorio (>=1) in un nodo valido. */\r\n relationships?: AtomicWritebackRelationship[];\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// Collezioni di nodi: la mappa nome-collezione -> tipo di nodo\r\n// ---------------------------------------------------------------------------\r\n\r\n/**\r\n * Le collezioni di nodi della radice, con il tipo che ciascuna contiene.\r\n *\r\n * Esiste perche' ogni utility che cammina il documento (derivazione degli archi, rinomina,\r\n * catalogo della palette) deve nominare le collezioni **una volta sola**: una collezione\r\n * dimenticata in uno dei tre posti non produce nessun errore, produce un arco che non\r\n * compare o un riferimento che resta rotto dopo una rinomina.\r\n */\r\nexport interface DpeNodeCollectionMap {\r\n dataSources: DataSourceNode;\r\n filters: FilterNode;\r\n joins: JoinNode;\r\n aggregates: AggregateNode;\r\n transforms: TransformNode;\r\n appends: AppendNode;\r\n hierarchyPaths: HierarchyPathNode;\r\n forecasts: ForecastNode;\r\n customNodes: CustomNode;\r\n writebacks: WritebackNode;\r\n atomicWritebacks: AtomicWritebackNode;\r\n}\r\n\r\nexport type DpeNodeCollectionName = keyof DpeNodeCollectionMap;\r\n\r\n/** Qualunque nodo della definizione. */\r\nexport type DpeAnyNode = DpeNodeCollectionMap[DpeNodeCollectionName];\r\n\r\n// L'ordine delle collezioni, scritto una volta sola. E' una tupla `as const` e non un array\r\n// annotato perche' il controllo qui sotto deve vederne i membri uno per uno; `satisfies` rifiuta\r\n// un nome che non sia una collezione.\r\nconst COLLECTION_ORDER = [\r\n 'dataSources',\r\n 'filters',\r\n 'joins',\r\n 'aggregates',\r\n 'transforms',\r\n 'appends',\r\n 'hierarchyPaths',\r\n 'forecasts',\r\n 'customNodes',\r\n 'writebacks',\r\n 'atomicWritebacks',\r\n] as const satisfies readonly DpeNodeCollectionName[];\r\n\r\n/** I membri che compaiono piu' di una volta in una tupla. */\r\ntype DpeRepeatedMembers<L extends readonly unknown[]> = L extends readonly [infer Head, ...infer Rest]\r\n ? (Head extends Rest[number] ? Head : never) | DpeRepeatedMembers<Rest>\r\n : never;\r\n\r\n/**\r\n * `true` se la tupla `L` nomina ogni membro di `U` una volta sola. Altrimenti un oggetto che\r\n * **nomina** i mancanti e i ripetuti: un semplice `false` fermerebbe la build senza dire perche'.\r\n */\r\ntype DpeListCoverage<U, L extends readonly unknown[]> = [\r\n Exclude<U, L[number]>,\r\n DpeRepeatedMembers<L>,\r\n] extends [never, never]\r\n ? true\r\n : { mancanti: Exclude<U, L[number]>; ripetuti: DpeRepeatedMembers<L> };\r\n\r\n// Non si legge mai: esiste per essere compilata. Se l'elenco perde o ripete un membro il tipo\r\n// smette di essere `true` e la build si ferma con «TS2322: Type 'boolean' is not assignable to\r\n// type '{ mancanti: \"atomicWritebacks\"; ripetuti: never; }'», con i nomi scritti dentro.\r\nconst COLLECTION_ORDER_IS_TOTAL: DpeListCoverage<DpeNodeCollectionName, typeof COLLECTION_ORDER> = true;\r\n\r\n/**\r\n * L'elenco dei nomi di collezione in forma di valore, nell'ordine in cui i nodi si leggono\r\n * (sorgenti -> trasformazioni -> sink). `keyof` non produce un array a runtime: questo\r\n * elenco e' l'unico posto in cui vive l'ordine. Lo leggono il cammino del documento\r\n * (`dpe-graph.ts`) e la palette, attraverso `DPE_NODE_CATEGORIES` che ne deriva.\r\n *\r\n * **Totale e senza ripetizioni, e a dirlo e' il compilatore.** Un array non si verifica contro\r\n * un'unione: una collezione aggiunta a `DpeNodeCollectionMap` e dimenticata qui compilerebbe, e\r\n * sparirebbe in silenzio dal cammino del documento (archi che non compaiono, rinomine\r\n * incomplete) e dalla palette. Il controllo `COLLECTION_ORDER_IS_TOTAL` trasforma l'omissione, e\r\n * la ripetizione, in un errore di build che nomina il membro.\r\n */\r\nexport const DPE_NODE_COLLECTIONS: readonly DpeNodeCollectionName[] = COLLECTION_ORDER;\r\n","/**\r\n * Diagnostiche — traduzione letterale di `Jace.DPE.Diagnostics` (`docs/contracts.md` §1).\r\n *\r\n * Tipi allineati **a mano** ai record C# (ADR 0007). L'editor **consuma** queste\r\n * diagnostiche e non ne produce: nessuna regola di dominio vive nel frontend, quindi in\r\n * questo file non ci sono ne' codici inventati ne' messaggi ricomposti. Il `message` arriva\r\n * dal backend, che e' l'unico posto in cui un messaggio viene composto.\r\n */\r\n\r\nexport type DiagnosticSeverity = 'Info' | 'Warning' | 'Error';\r\n\r\nexport interface DiagnosticPathSegment {\r\n /** `\"filters\"`, `\"criteria\"`, `\"fields\"`, ... */\r\n collection?: string;\r\n /** Nome del nodo/alias, se applicabile. */\r\n name?: string;\r\n /** Indice nella collezione, se applicabile. */\r\n index?: number;\r\n}\r\n\r\n/**\r\n * Percorso strutturato del rilievo.\r\n *\r\n * **Due forme, entrambe da gestire** (`contracts.md` §1, limite dichiarato): in\r\n * deserializzazione i nomi dei nodi non sono ancora noti e i segmenti portano solo l'indice\r\n * (`filters[0].sourceName`); dalla validazione portano il nome (`filters/OnlyActive`). Chi\r\n * naviga fino al campo deve accettare tutt'e due, altrimenti le diagnostiche del caricamento\r\n * di un file non sono cliccabili.\r\n */\r\nexport interface DiagnosticPath {\r\n segments?: DiagnosticPathSegment[];\r\n /** Nome della proprieta' in causa, camelCase. */\r\n field?: string;\r\n}\r\n\r\n/**\r\n * `arguments` e' un dizionario di stringhe con **chiavi canoniche** (`contracts.md` §1):\r\n * `node`, `field`, `column`, `alias`, `name`, `table`, `parameter`, `dialect`, `construct`,\r\n * `expected`, `actual`, `value`, `sequence`, `condition`, `token`, `truncated`. L'editor le\r\n * legge per costruire etichette, non per riscrivere il messaggio.\r\n */\r\nexport interface Diagnostic {\r\n /** `DiagnosticCodes.*` del backend. Il frontend non ne dichiara l'elenco: e' del backend. */\r\n code: string;\r\n severity: DiagnosticSeverity;\r\n path?: DiagnosticPath;\r\n arguments?: Record<string, string>;\r\n /** Derivato da `code` + `arguments`, cultura invariante. */\r\n message: string;\r\n}\r\n\r\n/**\r\n * L'unico codice che significa \"hai fatto bene, siamo noi\": nodo valido ma non ancora\r\n * compilabile (`Forecast`, `CustomNode`, costrutto non supportato — **I10**). Va distinto\r\n * visivamente dai `DPE_MODEL_*`, che invece l'utente deve correggere.\r\n *\r\n * E' l'unica costante di codice diagnostico che la libreria dichiara, e non e' una\r\n * duplicazione del dominio: e' la sola distinzione che cambia **come si rende** un rilievo.\r\n */\r\nexport const DPE_NODE_NOT_COMPILABLE = 'DPE_NODE_NOT_COMPILABLE';\r\n\r\n/**\r\n * Il codice che il contratto (`contracts.md` §5 e §7) prescrive di mostrare **in testa** quando il\r\n * catalogo delle formule non e' stato prodotto: nomina la sorgente il cui schema non si risolve,\r\n * mentre le diagnostiche strutturali che lo accompagnano ne dicono la causa.\r\n *\r\n * Seconda e ultima costante di codice dichiarata dalla libreria, per lo stesso criterio della\r\n * prima: non e' un elenco di dominio duplicato, e' l'unica cosa che cambia **come si rende** un\r\n * sacco di rilievi. Senza di essa la riga che l'architetto ha aggiunto apposta — «una riga da\r\n * mostrare in testa» — finirebbe in mezzo alle altre, che e' il rumore indistinguibile che il\r\n * codice esiste per evitare.\r\n */\r\nexport const DPE_CATALOG_UNAVAILABLE = 'DPE_CATALOG_UNAVAILABLE';\r\n\r\n/** Rappresentazione testuale del percorso, nella forma usata dai messaggi del backend. */\r\nexport function diagnosticPathToString(path: DiagnosticPath | undefined): string {\r\n if (!path) {\r\n return '';\r\n }\r\n const segments = path.segments ?? [];\r\n let text = '';\r\n for (const segment of segments) {\r\n if (segment.collection) {\r\n text += text.length > 0 ? `/${segment.collection}` : segment.collection;\r\n }\r\n if (segment.name) {\r\n text += `/${segment.name}`;\r\n } else if (segment.index !== undefined) {\r\n text += `[${segment.index}]`;\r\n }\r\n }\r\n if (path.field) {\r\n text += text.length > 0 ? `.${path.field}` : path.field;\r\n }\r\n return text;\r\n}\r\n","/**\r\n * Tipi di richiesta/risposta del service DPE — traduzione letterale di `docs/contracts.md`\r\n * §2 (provider di metadati), §3 (SQL emesso) e §5 (il service).\r\n *\r\n * Allineati **a mano** ai record C# (ADR 0007). Cio' che non e' trasportabile non c'e':\r\n * `DpeContext` porta i provider dell'host (`ITableMetadataProvider`, `IDataQueryProvider`,\r\n * `IArtifactSink`, `ILogger`, `IClock`) e vive **dentro** il backend, quindi non ha un\r\n * corrispettivo TypeScript. Lo stesso vale per `Execute`: l'editor non esegue, non\r\n * materializza artefatti e non ha bisogno di `DpeExecuteResult`.\r\n */\r\n\r\nimport {\r\n ColumnType,\r\n CsvArtifactOptions,\r\n DefinitionRunMode,\r\n DefinitionStatus,\r\n JsonArtifactOptions,\r\n LogicalType,\r\n SqlDialectKind,\r\n WritebackFailureHandling,\r\n WritebackFieldRole,\r\n WritebackOperation,\r\n WritebackTargetKind,\r\n} from './dpe-definition.model';\r\nimport { Diagnostic } from './dpe-diagnostics.model';\r\n\r\n// ---------------------------------------------------------------------------\r\n// §7 La dichiarazione dell'ambiente (ADR 0017)\r\n// ---------------------------------------------------------------------------\r\n\r\n/**\r\n * Cio' che l'ambiente ospite **dichiara** all'editor. Non e' un tipo del backend C#: `IDpeService`\r\n * non cambia (ADR 0017), e l'host lo compone con cio' che gia' sa della propria installazione.\r\n *\r\n * Nasce con un solo campo e si estende **per aggiunta**: e' il posto previsto per i fatti\r\n * dell'ambiente, non un contenitore da riempire in anticipo.\r\n */\r\nexport interface DpeEnvironment {\r\n /** Il dialetto del database di destinazione. Dichiarato, mai scelto dall'utente dell'editor. */\r\n readonly dialect: SqlDialectKind;\r\n /**\r\n * Nome del linguaggio in cui si scrivono le espressioni, dal compilatore di formule registrato\r\n * dall'ospite (ADR 0031/0036).\r\n *\r\n * Serve **all'etichetta del campo**, e per questo sta qui e non nel catalogo: il catalogo puo'\r\n * legittimamente non arrivare — il compilatore non descrive il proprio linguaggio\r\n * (`ProvidesCatalog == false`), oppure il dialetto della definizione non e' fra quelli che\r\n * copre e allora il catalogo e' nullo — mentre il nome del linguaggio si sa sempre. Un editor\r\n * che leggesse il nome dal catalogo resterebbe muto proprio nei casi in cui l'utente ha piu'\r\n * bisogno di sapere in che lingua sta scrivendo.\r\n *\r\n * Assente = l'editor non nomina nessun linguaggio. Non nomina il **dialetto** al suo posto: il\r\n * dialetto non c'entra piu' con cio' che si scrive in quel campo (ADR 0031).\r\n */\r\n readonly expressionLanguage?: string;\r\n /**\r\n * Il compilatore descrive il proprio linguaggio. Falso o assente: **non chiamare**\r\n * `getFormulaCatalog`.\r\n *\r\n * Si interroga **prima** di chiamare, per la stessa ragione per cui la disponibilita' di un\r\n * comando si sa prima del clic (`contracts.md` §7): una chiamata che si sa inutile costa al\r\n * backend una passata completa di grafo e di metadati per restituire un catalogo vuoto.\r\n */\r\n readonly providesFormulaCatalog?: boolean;\r\n /**\r\n * Le **forme** che il compilatore registrato sa produrre (`Capabilities.SupportedForms`, ADR\r\n * 0031). Incrociata con `DpeNodeSchemaInfo.requiredForm` — che e' per **nodo** e arriva con la\r\n * validazione — permette di **disabilitare** un campo formula che non potra' mai essere\r\n * compilato.\r\n *\r\n * Le due meta' stanno in due posti perche' hanno due sorgenti e due vite: la forma richiesta\r\n * viene dalla **definizione**, le forme supportate dal **compilatore registrato**, cioe'\r\n * dall'installazione, come `expressionLanguage` e `providesFormulaCatalog` qui accanto.\r\n * L'editor le incrocia e non deduce nessuna delle due.\r\n *\r\n * **Assente (o `null`) significa: non si disabilita niente.** Non e' un ripiego prudente, e' la\r\n * politica: non sapere quali forme il compilatore copra non autorizza a indovinare, e un campo\r\n * spento per ignoranza e' peggio di un campo acceso che il backend rifiutera' dicendo perche'.\r\n * Un elenco **vuoto** e' un'altra cosa — e' una dichiarazione, e dice «nessuna forma».\r\n *\r\n * Opzionale **e** nullabile per la regola dei tipi di rispecchiamento: descrivono cio' che\r\n * arriva **sul filo**, e l'ospite di riferimento ignora i nulli come politica globale, quindi la\r\n * proprieta' puo' non comparire affatto.\r\n */\r\n readonly supportedForms?: readonly DpeExpressionForm[] | null;\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// §7 Catalogo del linguaggio di formule (ADR 0031, 0036)\r\n// ---------------------------------------------------------------------------\r\n\r\n/**\r\n * La **forma** che i siti di espressione di un nodo pretendono (ADR 0031 §1).\r\n *\r\n * Attraversa il confine su `DpeNodeSchemaInfo.requiredForm` per una ragione sola: dedurla dal tipo\r\n * di nodo — «una trasformazione `Expression` vuole un valore, il filtro di un writeback vuole un\r\n * predicato» — sarebbe una **regola di dominio in TypeScript**, che e' vietata. La sa il backend, e\r\n * la dice.\r\n *\r\n * Il tipo sta in questa sezione e non accanto agli schemi perche' e' la **stessa** enumerazione che\r\n * `DpeEnvironment.supportedForms` dichiara: sono i due operandi dell'incrocio, e un'enumerazione\r\n * dichiarata due volte diverge.\r\n */\r\nexport type DpeExpressionForm = 'Value' | 'Predicate' | 'WindowFunction';\r\n\r\n/**\r\n * L'**involucro** di `getFormulaCatalog`, rispecchiamento di `DpeFormulaCatalogResult`\r\n * (`contracts.md` §7, normativo). La rotta consegna questo, non il catalogo nudo.\r\n *\r\n * Il motivo e' misurato, non di principio: il service distingue **tre** esiti e il filo ne\r\n * consegnava **due** — o un catalogo o un errore di trasporto — quindi *dialetto non coperto* e\r\n * *backend rotto* arrivavano all'editor identici, e il degrado era silenzioso.\r\n *\r\n * **Il discriminante e' `diagnostics`, non `catalog`:** diagnostiche non vuote significa\r\n * fallimento, e si mostrano; altrimenti un catalogo c'e', e la tavolozza si mostra se ha\r\n * contenuto. In particolare un compilatore che non descrive il proprio linguaggio consegna il\r\n * catalogo **presente e vuoto**, con `languageName` valorizzato e zero diagnostiche: chi ramasse\r\n * su `catalog == null` non entrerebbe **mai** in quel caso.\r\n *\r\n * La derivazione dei tre esiti sta in `dpeFormulaCatalogOutcome`, una funzione pura, e non in un\r\n * `if` dentro un componente.\r\n */\r\nexport interface DpeFormulaCatalogResult {\r\n /**\r\n * Il catalogo. **Assente o `null` significa una cosa sola: non e' stato prodotto** — non\r\n * «prodotto vuoto», che e' un esito diverso e si riconosce dagli elenchi vuoti.\r\n *\r\n * Opzionale **e** nullabile insieme, e non e' pignoleria: un tipo di rispecchiamento descrive\r\n * cio' che arriva **sul filo**, non cio' che il record C# dichiara, e ospiti diversi\r\n * serializzano diversamente. L'ospite di riferimento ignora i nulli come politica globale,\r\n * quindi la proprieta' non compare affatto e arriva `undefined` mentre un tipo con il solo\r\n * `| null` prometterebbe altro. `check:modelli` confronta i **nomi** dei campi e questa classe\r\n * di divergenza non la vede.\r\n */\r\n readonly catalog?: DpeFormulaCatalog | null;\r\n /**\r\n * Le diagnostiche del service. Mai nulla; **vuota** quando il compilatore semplicemente non\r\n * descrive il proprio linguaggio, che non e' un errore.\r\n *\r\n * `DPE_CATALOG_UNAVAILABLE` e' la riga da mostrare **in testa**: nomina la sorgente il cui\r\n * schema non si risolve, e le strutturali che la seguono ne dicono la causa. Non si filtrano\r\n * per pertinenza — l'errore che spiega lo schema puo' stare a piu' salti di distanza — quindi\r\n * diventano dettaglio espandibile invece di rumore indistinguibile.\r\n */\r\n readonly diagnostics: readonly Diagnostic[];\r\n readonly hasErrors: boolean;\r\n /*\r\n * `requiredForm` **non sta piu' qui**: e' su `DpeNodeSchemaInfo`, dentro il risultato di\r\n * `validate`. Stava qui per parentela di comodita' — il catalogo si chiede per nodo — ma le due\r\n * cose rispondono a due domande diverse: questo involucro dice «cio' che il compilatore offre», e\r\n * arriva **solo quando il compilatore risponde**; la forma e' «cio' che la libreria ha derivato\r\n * su quel nodo», e la libreria la sa senza chiedere niente a nessuno.\r\n *\r\n * La differenza si misurava: un ospite con `providesFormulaCatalog` falso non fa chiamare questa\r\n * rotta all'editor, quindi la forma non arrivava **mai** — e l'incrocio con\r\n * `DpeEnvironment.supportedForms` non poteva scattare proprio dove il compilatore e' piu'\r\n * laconico. Non resta anche qui: due copie dello stesso fatto divergono (ADR 0018).\r\n */\r\n}\r\n\r\n/**\r\n * Rispecchiamento sul filo di `Jace.DPE.Expressions.DpeFormulaCatalog`: cosa il linguaggio di\r\n * formule dell'ospite mette a disposizione **nel punto** in cui si sta scrivendo.\r\n *\r\n * **Non e' una regola di dominio e non ne diventa una** (`contracts.md` §7). L'editor lo usa per\r\n * *mostrare* cosa esiste; non lo usa per validare cio' che l'utente scrive. La grammatica e' del\r\n * compilatore, il frontend non la conosce, e nessuna validazione di formula viene scritta in\r\n * TypeScript — per nessun motivo, nemmeno «solo per il caso banale»: una regola duplicata diverge,\r\n * e quando diverge l'editor mente all'utente.\r\n *\r\n * Un catalogo **vuoto e' legittimo** (ADR 0036 §5) e non e' un errore: significa che il\r\n * compilatore non descrive il proprio linguaggio, e l'editor mostra un campo di testo semplice.\r\n */\r\nexport interface DpeFormulaCatalog {\r\n /**\r\n * Nome del linguaggio secondo il compilatore. **Non** e' la fonte dell'etichetta del campo:\r\n * quella e' `DpeEnvironment.expressionLanguage`, che arriva anche quando il catalogo non arriva.\r\n */\r\n readonly languageName: string;\r\n readonly functions: readonly DpeFormulaFunction[];\r\n readonly operators: readonly DpeFormulaOperator[];\r\n readonly symbols: readonly DpeFormulaSymbol[];\r\n}\r\n\r\nexport interface DpeFormulaFunction {\r\n /** Gruppo con cui l'elenco raggruppa la voce. */\r\n readonly group: string;\r\n readonly name: string;\r\n readonly description?: string;\r\n /**\r\n * Assente quando il tipo di ritorno **dipende dagli argomenti** e un elenco statico non puo'\r\n * dichiararlo (ADR 0036 §4). Assente significa «non si sa qui», non `String`.\r\n */\r\n readonly returnType?: ColumnType;\r\n readonly parameters: readonly DpeFormulaFunctionParameter[];\r\n}\r\n\r\nexport interface DpeFormulaFunctionParameter {\r\n readonly name: string;\r\n readonly description?: string;\r\n readonly type?: ColumnType;\r\n readonly isOptional?: boolean;\r\n /**\r\n * Il parametro appartiene a un gruppo che **si ripete** (funzione variadica). Un solo campo al\r\n * posto della coppia gruppo/ordine del sistema di origine: il matching delle firme e' lavoro del\r\n * compilatore, a un elenco per l'utente serve sapere che il gruppo si ripete.\r\n */\r\n readonly isRepeating?: boolean;\r\n}\r\n\r\nexport interface DpeFormulaOperator {\r\n readonly group: string;\r\n readonly symbol: string;\r\n readonly description?: string;\r\n}\r\n\r\n/**\r\n * Provenienza di un simbolo. Le tre sono strutturalmente diverse e l'elenco le raggruppa: una\r\n * colonna a monte, un parametro di runtime della definizione e una costante del linguaggio non si\r\n * correggono nello stesso modo quando il nome e' sbagliato.\r\n */\r\nexport type DpeFormulaSymbolKind = 'Column' | 'Parameter' | 'Constant';\r\n\r\nexport interface DpeFormulaSymbol {\r\n readonly name: string;\r\n readonly description?: string;\r\n /** `null`/assente quando il compilatore non sa mapparlo sui sei tipi logici: una mancanza\r\n * dichiarata e' meglio di una bugia. */\r\n readonly type?: ColumnType;\r\n readonly kind: DpeFormulaSymbolKind;\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// §7 Catalogo delle definizioni (ADR 0019)\r\n// ---------------------------------------------------------------------------\r\n\r\n/**\r\n * Riga di catalogo: identita' e fatti che l'ospite conosce **senza** caricare la definizione.\r\n *\r\n * Distinto da `DpeDefinition` per costruzione, e non per comodita': elencare non deve costare N\r\n * caricamenti completi. Non ha un corrispettivo in `IDpeService` e non deve averlo — il service\r\n * valida, compila ed esegue; dove le definizioni vivano e' dell'ospite (ADR 0007), che non ha\r\n * ne' orologio ne' archiviazione lato libreria.\r\n *\r\n * I quattro campi in coda sono facoltativi in senso pieno: assenti, la riga **non li mostra e\r\n * non li stima**. Contare i nodi lato client vorrebbe dire caricare tutte le definizioni per\r\n * disegnare un elenco, e dedurre `updatedAt` e' impossibile.\r\n */\r\nexport interface DpeDefinitionSummary {\r\n /** La chiave. Non esiste un id distinto dal nome (ADR 0019): rinominare e' cambiare chiave. */\r\n name: string;\r\n label: string;\r\n description?: string;\r\n dialect: SqlDialectKind;\r\n status?: DefinitionStatus;\r\n runMode?: DefinitionRunMode;\r\n isTemplate?: boolean;\r\n /** ISO 8601. Lo scrive l'ospite: la libreria non ha orologio. Assente = non mostrato. */\r\n updatedAt?: string;\r\n updatedBy?: string;\r\n /** Numero di nodi. Lo conta l'ospite, **mai** il client. Assente = non mostrato. */\r\n nodeCount?: number;\r\n /**\r\n * Esito dell'ultima validazione **noto all'ospite**: riportato, non ricalcolato. Puo' essere\r\n * vecchio, quindi la riga lo mostra come stato noto e non come verita' — il frontend non\r\n * valida nulla, e dedurlo qui sarebbe una validazione fatta dal client per interposta persona.\r\n */\r\n hasErrors?: boolean;\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// §2 Provider di metadati\r\n// ---------------------------------------------------------------------------\r\n\r\n/**\r\n * Tre esiti, **non due** (ADR 0004): \"dichiarata\", \"dichiarata senza campi\" e\r\n * \"sconosciuta\" sono situazioni diverse e l'editor le mostra diverse. Chi implementa\r\n * l'interfaccia non appiattisce `Unknown` in una lista di campi vuota.\r\n */\r\nexport type TableLookupOutcome = 'Declared' | 'DeclaredWithoutFields' | 'Unknown';\r\n\r\nexport interface TableSummary {\r\n name: string;\r\n description?: string;\r\n}\r\n\r\nexport interface FieldRelationship {\r\n targetTableName: string;\r\n targetFieldName: string;\r\n}\r\n\r\nexport interface FieldDescriptor {\r\n name: string;\r\n description?: string;\r\n type: ColumnType;\r\n isKey: boolean;\r\n isNullable: boolean;\r\n /** Assente quando il campo non e' una relazione. */\r\n relationship?: FieldRelationship;\r\n}\r\n\r\nexport interface TableDescriptor {\r\n name: string;\r\n description?: string;\r\n fields?: FieldDescriptor[];\r\n}\r\n\r\nexport interface TableLookupResult {\r\n outcome: TableLookupOutcome;\r\n /** Presente solo con `outcome === 'Declared'`. */\r\n table?: TableDescriptor;\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// §3 SQL emesso\r\n// ---------------------------------------------------------------------------\r\n\r\nexport interface SqlParameterValue {\r\n /** Senza prefisso di dialetto: `\"p0\"`, `\"param_StartDate\"`. */\r\n name: string;\r\n type: LogicalType;\r\n /** `null` ammesso. Valore opaco per l'editor, che lo mostra e non lo interpreta. */\r\n value: unknown;\r\n}\r\n\r\nexport interface SqlStatement {\r\n /** Porta i marcatori nella forma del dialetto (`@p0`, `:p0`, `$1`). */\r\n text: string;\r\n parameters?: SqlParameterValue[];\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// §5 Il service: Validate\r\n// ---------------------------------------------------------------------------\r\n\r\nexport interface DpeColumnInfo {\r\n name: string;\r\n type: ColumnType;\r\n /**\r\n * Informativa (**I4**): serve alla vista dello schema, non blocca la compilazione. Il\r\n * frontend non la calcola — arriva da qui.\r\n */\r\n isNullable: boolean;\r\n}\r\n\r\n/**\r\n * **Cio' che la libreria ha derivato su un nodo**: schema di uscita, compilabilita' e forma pretesa\r\n * dai suoi siti di espressione. Arriva con **ogni** validazione, e da ADR 0041 con **una voce per\r\n * ogni nodo della definizione, sempre** — anche per i nodi che il grafo non ha potuto ordinare.\r\n *\r\n * La presenza di una voce dice «questo nodo esiste, ed ecco cio' che la libreria ha potuto derivare\r\n * su di esso»; **non** dice «il suo schema e' noto». Quello lo dice `hasSchema`, e solo quello.\r\n */\r\nexport interface DpeNodeSchemaInfo {\r\n nodeName: string;\r\n /**\r\n * Colonne di uscita. **Vuota non significa \"nessuna colonna\"** finche' non si e' letto\r\n * `hasSchema`: e' vuota anche quando lo schema e' ignoto. Contarle senza guardare `hasSchema`\r\n * afferma un fatto che il backend non ha affermato — ed e' il difetto misurato sul canvas prima\r\n * dell'ADR 0041.\r\n *\r\n * **Obbligatoria**, come nel contratto (`contracts.md` §5): in C# e' `IReadOnlyList` mai nulla e\r\n * arriva come `[]` quando e' vuota, quindi un ospite che omette la collezione vuota e' fuori\r\n * contratto e non un caso da assecondare nel tipo. Dichiararla opzionale costringeva a un `?? []`\r\n * a ogni lettura, e quel `?? []` e' **indistinguibile fra «vuota» e «non arrivata»** — cioe' la\r\n * stessa ambiguita' che l'ADR 0041 ha tolto da `Columns`, riaperta un livello piu' in basso e in\r\n * TypeScript. La regola generale (`contracts.md` §7): cio' che puo' mancare si dichiara opzionale\r\n * **e** nullabile insieme, e una collezione non e' fra quelle cose.\r\n */\r\n columns: DpeColumnInfo[];\r\n /**\r\n * Il discriminante fra «schema derivato» e «schema ignoto» (ADR 0041).\r\n *\r\n * - `true` = `columns` e' un **fatto**. Vuota vuol dire davvero zero colonne — stato\r\n * raggiungibile, e gia' diagnosticato con `DPE_SCHEMA_EMPTY`.\r\n * - `false` = schema **ignoto**: la derivazione non e' arrivata a una risposta (fallita qui o a\r\n * monte, oppure nodo dentro un ciclo). `columns` e' vuota e **non significa niente**.\r\n *\r\n * **Assente = `false`.** Un ospite che serializza omettendo i falsi non manda affatto la\r\n * proprieta', e qui l'omissione coincide con la risposta prudente: e' il motivo per cui il campo\r\n * si chiama `hasSchema` e non `isSchemaUnknown`. Chi legge confronta quindi con `=== true`, mai\r\n * per veridicita', e non lo deduce da `columns.length` — dedurlo sarebbe rimettere in TypeScript\r\n * la regola che questo campo esiste per portare.\r\n */\r\n hasSchema?: boolean;\r\n /**\r\n * `false` = nodo **valido ma non compilabile** (**I10**): e' un limite nostro, non un\r\n * errore dell'utente, e va reso diverso da un nodo invalido.\r\n */\r\n isCompilable: boolean;\r\n /**\r\n * La forma pretesa dai siti di espressione di questo nodo, oppure assente/`null` se il nodo non\r\n * ne ha — che e' uno stato normale, non una mancanza.\r\n *\r\n * Sta qui e non sul risultato del catalogo perche' e' una cosa che la **libreria deriva** dal\r\n * tipo del nodo (ADR 0031 §1), non una cosa che il **compilatore offre**: l'editor la riceve\r\n * sempre, anche quando il catalogo non lo chiede affatto. Dedurla lato editor sarebbe una regola\r\n * di dominio in TypeScript, che e' vietata; incrociarla con `DpeEnvironment.supportedForms`\r\n * permette invece di **disabilitare** un campo che il compilatore registrato non compilera' mai.\r\n *\r\n * Opzionale **e** nullabile: un tipo di rispecchiamento descrive cio' che arriva **sul filo**, e\r\n * l'ospite di riferimento omette i nulli come politica globale — quindi la proprieta' puo' non\r\n * comparire affatto.\r\n */\r\n requiredForm?: DpeExpressionForm | null;\r\n}\r\n\r\nexport interface DpeWritebackStep {\r\n writebackName: string;\r\n sequence: number;\r\n target: WritebackTargetKind;\r\n /** Assente per i target `Json`/`Csv`. */\r\n operation?: WritebackOperation;\r\n tableName?: string;\r\n /**\r\n * Campo di corrispondenza dell'`Upsert`; assente per ogni altra operazione. E' un\r\n * `targetFieldName`: si ritrova in `targetMapping` per risalire alla colonna del result set\r\n * (ADR 0016).\r\n */\r\n externalIdFieldName?: string;\r\n /** Presente se il writeback appartiene a un gruppo atomico. */\r\n atomicGroupName?: string;\r\n /**\r\n * Il valore **efficace** per `target === 'Database'` (ADR 0040): membro di un gruppo atomico ->\r\n * `Atomic`, altrimenti il valore dichiarato sul nodo, altrimenti `PerRecord`.\r\n *\r\n * **Assente per `Json`/`Csv`**, e segue la regola comune a tutti i campi dello `Step` che\r\n * istruiscono chi scrive — `operation`, `tableName`, `externalIdFieldName`,\r\n * `processingGroupFieldName`, `tenantColumnName`: sono assenti quando l'ospite non scrive.\r\n * L'assenza dice una cosa precisa, «questo non lo scrivi tu», e dirla e' meglio di un\r\n * `PerRecord` che su un artefatto sarebbe pure falso: un file si scrive per intero o per niente.\r\n *\r\n * Non e' il campo del nodo con un altro nome. `WritebackNode.failureHandling` e' cio' che\r\n * l'autore ha scritto, questo e' cio' che l'host deve fare: ricavarlo qui incrociando\r\n * `atomicGroupName` con il valore dichiarato e con il default vorrebbe dire reimplementare tre\r\n * regole di dominio nell'editor, che e' esattamente cio' che l'editor non fa. Se il dato serve,\r\n * arriva di qui.\r\n */\r\n failureHandling?: WritebackFailureHandling;\r\n /**\r\n * Dichiarato, **non implementato dalla libreria**: il change tracking e' dell'host\r\n * (ADR 0011) e non influenza l'SQL generato. Va mostrato come una dichiarazione che l'host\r\n * deve onorare, non come una cosa che il motore fa.\r\n */\r\n onlyChangedRows?: boolean;\r\n}\r\n\r\nexport interface DpeValidateResult {\r\n hasErrors: boolean;\r\n /** Esaustive (**I6**): l'elenco completo, non il primo rilievo. */\r\n diagnostics?: Diagnostic[];\r\n /** Ordine topologico. */\r\n schemas?: DpeNodeSchemaInfo[];\r\n executionOrder?: string[];\r\n writebackSequence?: DpeWritebackStep[];\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// §5 Il service: Compile\r\n// ---------------------------------------------------------------------------\r\n\r\n/**\r\n * Valore di un parametro di runtime per una compilazione (`DpeParameterValue`).\r\n *\r\n * `value` e' **opaco**: il tipo dichiarato del parametro lo conosce il backend, che lo\r\n * interpreta secondo `ColumnType` in cultura invariante. L'editor lo trasporta e lo mostra,\r\n * non lo converte.\r\n */\r\nexport interface DpeParameterValue {\r\n name: string;\r\n value: unknown;\r\n}\r\n\r\n/**\r\n * Cio' che serve all'host per **eseguire** la scrittura che la libreria non esegue\r\n * (`contracts.md` §2, «`DpeFieldMappingInfo`»): senza, l'host riceve un result set e non sa\r\n * quale colonna va in quale campo ne' quale sia la chiave.\r\n */\r\nexport interface DpeFieldMappingInfo {\r\n /** Nome della colonna nel result set dello `SqlStatement`. */\r\n resultColumnName?: string;\r\n targetFieldName: string;\r\n role: WritebackFieldRole;\r\n /** Presente: il valore viene da un parametro, non dalla riga. */\r\n runtimeParameterName?: string;\r\n /** La destinazione genera il valore: non e' nel result set. */\r\n isAutoGenerated?: boolean;\r\n relationshipName?: string;\r\n parentName?: string;\r\n}\r\n\r\nexport interface DpeCompiledWriteback {\r\n writebackName: string;\r\n sequence: number;\r\n /**\r\n * L'intento di scrittura di questo writeback: operazione, tabella, chiave di corrispondenza,\r\n * gruppo atomico, target. **Mai assente** (`contracts.md` §5), ed e' la **stessa istanza** che\r\n * compare in `DpeCompileResult.writebackSequence` (ADR 0018).\r\n *\r\n * Sta qui e non duplicato campo per campo su questo tipo: senza di esso il client non ha modo di\r\n * sapere *cosa* fa uno statement — mostrerebbe l'SQL di un `Upsert` e di una `Delete` allo stesso\r\n * modo — e l'unica alternativa sarebbe incrociare `writebackSequence` per nome, cioe' ricostruire\r\n * a mano un collegamento che il backend manda gia' fatto.\r\n */\r\n step: DpeWritebackStep;\r\n statement: SqlStatement;\r\n resultColumns?: DpeColumnInfo[];\r\n /** Vedi {@link DpeFieldMappingInfo}: forma provvisoria. */\r\n targetMapping?: DpeFieldMappingInfo[];\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// §5 Il piano di artefatto (ADR 0029)\r\n// ---------------------------------------------------------------------------\r\n\r\n/**\r\n * Il formato dell'artefatto prodotto. Due soli valori: `Database` non e' un artefatto e qui e'\r\n * impossibile per costruzione — per questo il piano non riusa `WritebackTargetKind`.\r\n */\r\nexport type ArtifactKind = 'Json' | 'Csv';\r\n\r\n/**\r\n * Una colonna dell'artefatto, con i **due nomi** che l'ADR 0015 tiene distinti: `resultName`\r\n * lega la colonna del result set, `headerName` e' l'unico che viene scritto nel file.\r\n * Mostrarli come uno solo nasconderebbe proprio la divergenza che quell'ADR ha chiuso.\r\n */\r\nexport interface DpeArtifactColumn {\r\n /** Alias SQL: serve a legare la colonna del result set. */\r\n resultName: string;\r\n /** Nome authored: e' quello che finisce nel file (ADR 0015). */\r\n headerName: string;\r\n type: LogicalType;\r\n}\r\n\r\n/**\r\n * Il payload auto-sufficiente con cui l'ospite puo' **schedulare** la scrittura di un artefatto\r\n * (ADR 0029): lo accoda, lo rilegge dopo un riavvio e lo riporta a `WriteArtifact`.\r\n *\r\n * L'editor non lo esegue e non lo materializza — `DpeBuilderApi` non acquista nessuna primitiva\r\n * per questo — ma lo riceve dentro `DpeCompileResult.fileWritebacks`, e l'anteprima ne mostra\r\n * l'SQL di lettura.\r\n *\r\n * `tenant` del record C# **non** e' qui, e non per dimenticanza: l'ambito tenant non attraversa\r\n * il confine verso l'editor, per la stessa ragione per cui `getTable` reda la colonna di tenant\r\n * (ADR 0027). Un backend che lo serializzi comunque non e' un errore: il client lo ignora.\r\n */\r\nexport interface DpeArtifactPlan {\r\n /**\r\n * Timbro del formato del payload. Il piano puo' tornare indietro **vecchio** da una coda, e un\r\n * valore ignoto viene rifiutato dal backend invece che tollerato.\r\n */\r\n formatVersion: number;\r\n definitionName: string;\r\n /** E' anche il nome passato al sink dell'ospite: il piano non porta un `artifactName` distinto. */\r\n writebackName: string;\r\n sequence: number;\r\n kind: ArtifactKind;\r\n /** Lo legge l'ospite per scegliere la connessione, non la libreria. */\r\n dialect: SqlDialectKind;\r\n /** SQL di **lettura**: nessun artefatto e' stato prodotto. */\r\n statement: SqlStatement;\r\n columns?: DpeArtifactColumn[];\r\n /** Presenti solo per il `kind` corrispondente. */\r\n jsonOptions?: JsonArtifactOptions;\r\n csvOptions?: CsvArtifactOptions;\r\n}\r\n\r\nexport interface DpeCompileResult {\r\n hasErrors: boolean;\r\n diagnostics?: Diagnostic[];\r\n databaseWritebacks?: DpeCompiledWriteback[];\r\n /**\r\n * ADR 0029: **piani di artefatto**, non writeback compilati. Portano gia' kind, opzioni e\r\n * intestazioni authored, cioe' tutto cio' che serve a scrivere il file; il `step` non c'e'\r\n * perche' per un file sarebbe fatto di soli campi nulli. L'SQL di lettura non e' ancora stato\r\n * eseguito.\r\n */\r\n fileWritebacks?: DpeArtifactPlan[];\r\n writebackSequence?: DpeWritebackStep[];\r\n}\r\n","/**\r\n * Quali primitive **opzionali** l'ospite implementa davvero — `contracts.md` §7, normativo.\r\n *\r\n * La rilevazione e' per **identita' del metodo**: se il metodo dell'istanza e' ancora quello del\r\n * prototipo di `DpeBuilderApi`, l'ospite non lo ha sovrascritto e la primitiva non c'e'. E'\r\n * esatta, non richiede una primitiva di interrogazione e non aggiunge un giro di rete.\r\n *\r\n * Perche' un solo posto: la disponibilita' va saputa **prima del clic**. Lo schema precedente —\r\n * partire da «disponibile» e scoprire l'assenza dal primo `MissingService` — mostra un comando\r\n * attivo che fallisce alla pressione, cioe' insegna a non fidarsi dei bottoni, e per giunta\r\n * costringe ogni consumatore a ripetere lo stesso trattamento dell'errore.\r\n *\r\n * Regola per chi implementa un host (normativa, `contracts.md` §7): **chi non supporta una\r\n * primitiva non la sovrascrive**. Una sovrascrittura che lancia subito non equivale all'assenza —\r\n * l'editor la considera disponibile e mostra il comando attivo.\r\n *\r\n * **Dichiarazione sottrattiva (ADR 0020).** Da quando la libreria spedisce un client HTTP che\r\n * implementa tutte le primitive, l'identita' da sola non basta: chi eredita quel client\r\n * non puo' dis-sovrascrivere un metodo. Disponibile diventa quindi «**sovrascritta** e **non\r\n * elencata** in `unsupportedPrimitives`». La seconda meta' e' solo sottrattiva — non rende\r\n * disponibile cio' che non e' implementato — e resta calcolata in questo file e in nessun altro.\r\n */\r\n\r\nimport { DpeBuilderApi } from '../api/dpe-builder-api';\r\n\r\n/** Le primitive che un ambiente ospite puo' legittimamente non offrire. */\r\nexport type DpeOptionalPrimitive =\r\n | 'compilePreview'\r\n | 'load'\r\n | 'save'\r\n | 'getEnvironment'\r\n | 'getFormulaCatalog'\r\n | 'listDefinitions'\r\n | 'createDefinition'\r\n | 'deleteDefinition';\r\n\r\ntype MethodBag = Record<string, unknown>;\r\n\r\nexport function dpeIsPrimitiveAvailable(\r\n api: DpeBuilderApi,\r\n primitive: DpeOptionalPrimitive,\r\n): boolean {\r\n // Prima la dichiarazione dell'ospite: se il servizio non c'e', non conta chi ha scritto il\r\n // metodo. E' l'unico modo per un ospite di spegnere una primitiva che ha **ereditato**.\r\n if (api.unsupportedPrimitives?.includes(primitive)) {\r\n return false;\r\n }\r\n const base = DpeBuilderApi.prototype as unknown as MethodBag;\r\n const implementation = (api as unknown as MethodBag)[primitive];\r\n return typeof implementation === 'function' && implementation !== base[primitive];\r\n}\r\n\r\n/** L'insieme delle primitive disponibili: comodo per fotografarlo una volta alla costruzione. */\r\nexport interface DpeCapabilities {\r\n readonly compilePreview: boolean;\r\n readonly load: boolean;\r\n readonly save: boolean;\r\n readonly getEnvironment: boolean;\r\n /**\r\n * Il catalogo del linguaggio di formule (ADR 0036). **Condizione necessaria e non sufficiente**\r\n * per chiederlo: l'altra meta' e' `DpeEnvironment.providesFormulaCatalog`, che e' un fatto del\r\n * compilatore e non del trasporto. Le due stanno separate perche' sono due cose diverse — «il\r\n * backend non serve questa rotta» e «il compilatore non descrive il proprio linguaggio» — e\r\n * mescolarle renderebbe impossibile distinguere un ambiente incompleto da un compilatore\r\n * laconico.\r\n */\r\n readonly getFormulaCatalog: boolean;\r\n readonly listDefinitions: boolean;\r\n readonly createDefinition: boolean;\r\n readonly deleteDefinition: boolean;\r\n}\r\n\r\nexport function dpeReadCapabilities(api: DpeBuilderApi): DpeCapabilities {\r\n return {\r\n compilePreview: dpeIsPrimitiveAvailable(api, 'compilePreview'),\r\n load: dpeIsPrimitiveAvailable(api, 'load'),\r\n save: dpeIsPrimitiveAvailable(api, 'save'),\r\n getEnvironment: dpeIsPrimitiveAvailable(api, 'getEnvironment'),\r\n getFormulaCatalog: dpeIsPrimitiveAvailable(api, 'getFormulaCatalog'),\r\n listDefinitions: dpeIsPrimitiveAvailable(api, 'listDefinitions'),\r\n createDefinition: dpeIsPrimitiveAvailable(api, 'createDefinition'),\r\n deleteDefinition: dpeIsPrimitiveAvailable(api, 'deleteDefinition'),\r\n };\r\n}\r\n\r\n/**\r\n * Disponibilita' di un comando, con **il motivo** quando manca: un bottone disabilitato che non\r\n * dice perche' manda a cercare un guasto che non c'e'.\r\n */\r\nexport interface DpeCommandAvailability {\r\n readonly isAvailable: boolean;\r\n /** Presente **solo** quando il comando non e' disponibile. Va nel `title` del bottone. */\r\n readonly reason?: string;\r\n}\r\n\r\n/**\r\n * Il comando di **creazione** e' l'unico che dipende da **due** primitive\r\n * (`contracts.md` §7, normativo): `createDefinition` per scrivere, e `getEnvironment` perche' il\r\n * dialetto della definizione nuova viene dall'ambiente (ADR 0017).\r\n *\r\n * Perche' senza `getEnvironment` si disabilita invece di ripiegare su un dialetto qualsiasi: il\r\n * dialetto decide con quali capacita' la definizione verra' compilata, e quali costrutti il\r\n * compilatore di formule copre (ADR 0031 §5: un dialetto fuori da `SupportedDialects` non compila\r\n * **nessuna** espressione), e chiederlo all'utente e' proprio la scelta che l'ADR 0017 ha tolto. Un\r\n * comando disabilitato che dice perche' e' meglio di entrambe; il rimedio per l'ospite e' una\r\n * riga, implementare `getEnvironment`.\r\n *\r\n * Sta qui e non dentro un componente per la stessa ragione delle altre: la disponibilita' si sa\r\n * **prima del clic**, e in un solo posto.\r\n */\r\nexport function dpeCreateCommandAvailability(\r\n capabilities: DpeCapabilities,\r\n): DpeCommandAvailability {\r\n const missing: string[] = [];\r\n if (!capabilities.createDefinition) {\r\n missing.push('createDefinition');\r\n }\r\n if (!capabilities.getEnvironment) {\r\n missing.push('getEnvironment');\r\n }\r\n if (missing.length === 0) {\r\n return { isAvailable: true };\r\n }\r\n return {\r\n isAvailable: false,\r\n reason:\r\n missing.length === 2\r\n ? 'Creazione non disponibile: questo ambiente non implementa createDefinition ne’ getEnvironment. ' +\r\n 'Senza la dichiarazione dell’ambiente il dialetto non si sceglie per ripiego (ADR 0017).'\r\n : missing[0] === 'createDefinition'\r\n ? 'Creazione non disponibile: questo ambiente non implementa createDefinition.'\r\n : 'Creazione non disponibile: questo ambiente non dichiara l’ambiente (getEnvironment), ' +\r\n 'quindi il dialetto della definizione nuova non e’ noto e non si sceglie per ripiego (ADR 0017).',\r\n };\r\n}\r\n","/**\r\n * Il grafo della definizione: **un solo posto** costruisce gli id di nodi, porte e archi, e\r\n * **un solo elenco** descrive dove vivono i riferimenti fra nodi.\r\n *\r\n * Perche' sta tutto qui. Un id di porta costruito in due modi diversi (canvas da un lato,\r\n * derivazione degli archi dall'altro) fa sparire un arco **senza nessun errore**: e' il\r\n * difetto piu' difficile da trovare in un editor a canvas. Allo stesso modo, un riferimento\r\n * dimenticato nell'elenco degli slot fa restare incoerente il documento dopo una rinomina,\r\n * anche li' in silenzio.\r\n *\r\n * Cosa **non** sta qui: nessuna regola di dominio. Questo file dice *dove* stanno i nomi dei\r\n * nodi a monte, non se un riferimento e' valido, se il grafo ha cicli (**I2**), se un nodo e'\r\n * a valle (**I3**) o quali colonne produca (**I4**). Quelle risposte arrivano da\r\n * `DpeBuilderApi.validate`.\r\n */\r\n\r\nimport {\r\n AggregateNode,\r\n AppendNode,\r\n AtomicWritebackNode,\r\n CustomNode,\r\n DataSourceNode,\r\n DPE_NODE_COLLECTIONS,\r\n DpeAnyNode,\r\n DpeDefinition,\r\n DpeNodeCollectionMap,\r\n DpeNodeCollectionName,\r\n FilterNode,\r\n ForecastNode,\r\n HierarchyPathNode,\r\n JoinNode,\r\n TransformNode,\r\n WritebackNode,\r\n} from '../model/dpe-definition.model';\r\n\r\n// ---------------------------------------------------------------------------\r\n// Id — l'unico posto che li costruisce\r\n// ---------------------------------------------------------------------------\r\n\r\n/**\r\n * Separatore dei componenti di un id. Un nome di nodo non lo contiene mai: i nomi sono\r\n * identificatori del modello e il backend li usa come identificatori SQL.\r\n */\r\nconst SEP = '::';\r\n\r\nexport function dpeNodeId(nodeName: string): string {\r\n return `node${SEP}${nodeName}`;\r\n}\r\n\r\n/**\r\n * Porta di ingresso. Una porta **per slot** (e per indice, sugli slot a lista): un `JoinNode`\r\n * ha due ingressi distinti e attaccare l'arco alla porta sbagliata scambia primaria e\r\n * secondaria, che e' un join diverso.\r\n */\r\nexport function dpeInputPortId(nodeName: string, slot: string, index?: number): string {\r\n const suffix = index === undefined ? '' : `${SEP}${index}`;\r\n return `in${SEP}${nodeName}${SEP}${slot}${suffix}`;\r\n}\r\n\r\n/** Porta di uscita: una sola per nodo, lo schema prodotto e' uno. */\r\nexport function dpeOutputPortId(nodeName: string): string {\r\n return `out${SEP}${nodeName}`;\r\n}\r\n\r\n/**\r\n * Id dell'arco: identifica la **destinazione** (nodo, slot, indice), non la coppia di nodi.\r\n * Due archi possono unire gli stessi due nodi su slot diversi — la primaria e la secondaria\r\n * di un self-join — e con un id per coppia uno dei due sparirebbe.\r\n */\r\nexport function dpeEdgeId(targetNodeName: string, slot: string, index?: number): string {\r\n const suffix = index === undefined ? '' : `${SEP}${index}`;\r\n return `edge${SEP}${targetNodeName}${SEP}${slot}${suffix}`;\r\n}\r\n\r\n/**\r\n * Gli inversi. Stanno **accanto** ai costruttori e non nel canvas: la libreria di canvas\r\n * restituisce gli id nei suoi eventi, e chi li legge deve tornare al nome del nodo con la\r\n * stessa convenzione con cui li ha scritti. Separarli e' il modo con cui un giorno cambierebbe\r\n * uno solo dei due.\r\n */\r\nexport function dpeParseNodeId(id: string): string | undefined {\r\n return id.startsWith(`node${SEP}`) ? id.slice(4 + SEP.length) : undefined;\r\n}\r\n\r\nexport function dpeParseOutputPortId(id: string): string | undefined {\r\n return id.startsWith(`out${SEP}`) ? id.slice(3 + SEP.length) : undefined;\r\n}\r\n\r\nexport function dpeParseInputPortId(\r\n id: string,\r\n): { nodeName: string; slot: string; index?: number } | undefined {\r\n if (!id.startsWith(`in${SEP}`)) {\r\n return undefined;\r\n }\r\n const parts = id.slice(2 + SEP.length).split(SEP);\r\n if (parts.length < 2) {\r\n return undefined;\r\n }\r\n const [nodeName, slot, rawIndex] = parts;\r\n const index = rawIndex === undefined ? undefined : Number(rawIndex);\r\n return {\r\n nodeName,\r\n slot,\r\n index: index === undefined || Number.isNaN(index) ? undefined : index,\r\n };\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// Slot dei riferimenti — l'unico elenco\r\n// ---------------------------------------------------------------------------\r\n\r\n/**\r\n * Uno slot e' un punto del nodo che contiene il nome di **un altro nodo**.\r\n *\r\n * `read` e `write` sono **posizionali e di lunghezza stabile**: `write(node, read(node))` non\r\n * cambia il nodo. E' la proprieta' che permette di implementare la rinomina come\r\n * `write(node, read(node).map(rinomina))` — cioe' con un solo cammino di codice, invece di\r\n * due elenchi (uno per leggere gli archi, uno per rinominare) che al primo campo aggiunto\r\n * diventano due elenchi diversi.\r\n */\r\nexport interface DpeSourceSlot<TNode> {\r\n readonly slot: string;\r\n /**\r\n * `true` quando lo slot e' un **arco del DAG** (il flusso dei dati). `false` per i\r\n * riferimenti che sono altro: i `sourceName` dei `JoinResultField` ripetono le sorgenti del\r\n * join, e le relazioni di un gruppo atomico dichiarano una transazione dell'host, non un\r\n * flusso di dati (l'ordinamento dei sink e' esplicito e separato dal DAG).\r\n */\r\n readonly isEdge: boolean;\r\n read(node: TNode): readonly (string | undefined)[];\r\n write(node: TNode, names: readonly (string | undefined)[]): TNode;\r\n}\r\n\r\nfunction single<TNode, TKey extends keyof TNode>(\r\n slot: string,\r\n key: TKey,\r\n isEdge = true,\r\n): DpeSourceSlot<TNode> {\r\n return {\r\n slot,\r\n isEdge,\r\n read: (node) => [node[key] as unknown as string | undefined],\r\n // Il cast copre solo la chiave calcolata: TypeScript non sa che `key` di `TNode` porta una\r\n // stringa, e la mappa dei tipi non e' esprimibile senza questo passaggio.\r\n write: (node, names) => ({ ...node, [key]: names[0] }) as unknown as TNode,\r\n };\r\n}\r\n\r\nfunction list<TNode, TKey extends keyof TNode>(\r\n slot: string,\r\n key: TKey,\r\n isEdge = true,\r\n): DpeSourceSlot<TNode> {\r\n return {\r\n slot,\r\n isEdge,\r\n read: (node) => ((node[key] as unknown as string[] | undefined) ?? []).slice(),\r\n // Uno slot a lista non si accorcia in scrittura: `write` riscrive i nomi al loro posto.\r\n // Aggiungere o togliere una sorgente e' una mutazione dello store, non una riscrittura.\r\n write: (node, names) =>\r\n ({ ...node, [key]: names.map((name) => name ?? '') }) as unknown as TNode,\r\n };\r\n}\r\n\r\nconst JOIN_RESULT_FIELDS_SLOT: DpeSourceSlot<JoinNode> = {\r\n slot: 'resultFields',\r\n isEdge: false,\r\n read: (node) => (node.fields ?? []).map((field) => field.sourceName),\r\n write: (node, names) =>\r\n node.fields === undefined\r\n ? node\r\n : {\r\n ...node,\r\n fields: node.fields.map((field, index) => ({ ...field, sourceName: names[index] ?? '' })),\r\n },\r\n};\r\n\r\n/**\r\n * Le relazioni di un gruppo atomico portano **due** nomi di writeback ciascuna, padre e\r\n * figlio. Sono letti e riscritti in coppia e in ordine: se questo slot mancasse, rinominare\r\n * un writeback lascerebbe il gruppo atomico che punta a un nome che non esiste piu' — senza\r\n * nessun errore lato editor, e con un rifiuto del backend che parla di un nodo che l'utente\r\n * non ha toccato.\r\n */\r\nconst ATOMIC_RELATIONSHIPS_SLOT: DpeSourceSlot<AtomicWritebackNode> = {\r\n slot: 'relationships',\r\n isEdge: false,\r\n read: (node) =>\r\n (node.relationships ?? []).flatMap((relationship) => [\r\n relationship.parentWritebackName,\r\n relationship.childWritebackName,\r\n ]),\r\n write: (node, names) =>\r\n node.relationships === undefined\r\n ? node\r\n : {\r\n ...node,\r\n relationships: node.relationships.map((relationship, index) => ({\r\n ...relationship,\r\n parentWritebackName: names[index * 2],\r\n childWritebackName: names[index * 2 + 1],\r\n })),\r\n },\r\n};\r\n\r\n/**\r\n * Gli slot di ogni collezione. Aggiungere un nodo significa aggiungere una riga qui.\r\n *\r\n * **Che ci sia una riga per ogni collezione lo garantisce il compilatore**, non un test: il tipo\r\n * e' mappato sull'unione `DpeNodeCollectionName`, quindi una collezione aggiunta al modello e\r\n * dimenticata qui ferma la build («Property '...' is missing»), e `DPE_NODE_COLLECTIONS`, con cui\r\n * il documento si cammina, e' a sua volta controllato totale. Il test che lo verificava a runtime\r\n * non poteva fallire, e per questo e' stato tolto: aggiungeva la convinzione di una copertura, non\r\n * la copertura.\r\n */\r\nexport const DPE_SOURCE_SLOTS: {\r\n readonly [K in DpeNodeCollectionName]: readonly DpeSourceSlot<DpeNodeCollectionMap[K]>[];\r\n} = {\r\n // Le sorgenti sono i punti d'ingresso del grafo: non riferiscono nessun nodo a monte.\r\n dataSources: [] as readonly DpeSourceSlot<DataSourceNode>[],\r\n filters: [single<FilterNode, 'sourceName'>('source', 'sourceName')],\r\n joins: [\r\n single<JoinNode, 'primarySourceName'>('primary', 'primarySourceName'),\r\n single<JoinNode, 'secondarySourceName'>('secondary', 'secondarySourceName'),\r\n JOIN_RESULT_FIELDS_SLOT,\r\n ],\r\n aggregates: [single<AggregateNode, 'sourceName'>('source', 'sourceName')],\r\n transforms: [single<TransformNode, 'sourceName'>('source', 'sourceName')],\r\n appends: [list<AppendNode, 'sources'>('sources', 'sources')],\r\n hierarchyPaths: [single<HierarchyPathNode, 'sourceName'>('source', 'sourceName')],\r\n forecasts: [single<ForecastNode, 'sourceName'>('source', 'sourceName')],\r\n customNodes: [list<CustomNode, 'sources'>('sources', 'sources')],\r\n writebacks: [single<WritebackNode, 'sourceName'>('source', 'sourceName')],\r\n atomicWritebacks: [ATOMIC_RELATIONSHIPS_SLOT],\r\n};\r\n\r\n// ---------------------------------------------------------------------------\r\n// Lettura del documento\r\n// ---------------------------------------------------------------------------\r\n\r\n/** Un nodo con la collezione da cui viene: senza, non si sa quali slot leggere. */\r\nexport interface DpeLocatedNode<K extends DpeNodeCollectionName = DpeNodeCollectionName> {\r\n collection: K;\r\n index: number;\r\n node: DpeNodeCollectionMap[K];\r\n}\r\n\r\n/** Tutti i nodi, nell'ordine delle collezioni (sorgenti -> trasformazioni -> sink). */\r\nexport function dpeAllNodes(definition: DpeDefinition): DpeLocatedNode[] {\r\n const result: DpeLocatedNode[] = [];\r\n for (const collection of DPE_NODE_COLLECTIONS) {\r\n const nodes = (definition[collection] ?? []) as DpeAnyNode[];\r\n nodes.forEach((node, index) => {\r\n result.push({ collection, index, node } as DpeLocatedNode);\r\n });\r\n }\r\n return result;\r\n}\r\n\r\nexport function dpeFindNode(definition: DpeDefinition, nodeName: string): DpeLocatedNode | undefined {\r\n return dpeAllNodes(definition).find((located) => located.node.name === nodeName);\r\n}\r\n\r\nexport interface DpeSourceReference {\r\n /** Nodo che contiene il riferimento (la destinazione dell'arco). */\r\n targetNodeName: string;\r\n collection: DpeNodeCollectionName;\r\n slot: string;\r\n /** Presente solo sugli slot a lista. */\r\n index?: number;\r\n isEdge: boolean;\r\n /** Nome riferito; assente quando lo slot e' vuoto (nodo non collegato). */\r\n sourceName?: string;\r\n}\r\n\r\n/** I riferimenti di un singolo nodo, in ordine di slot. */\r\nexport function dpeNodeSourceReferences(located: DpeLocatedNode): DpeSourceReference[] {\r\n const slots = DPE_SOURCE_SLOTS[located.collection] as readonly DpeSourceSlot<DpeAnyNode>[];\r\n const references: DpeSourceReference[] = [];\r\n for (const slot of slots) {\r\n const names = slot.read(located.node);\r\n const isPositional = names.length > 1 || slotIsPositional(slot);\r\n names.forEach((name, index) => {\r\n references.push({\r\n targetNodeName: located.node.name,\r\n collection: located.collection,\r\n slot: slot.slot,\r\n index: isPositional ? index : undefined,\r\n isEdge: slot.isEdge,\r\n sourceName: name === '' ? undefined : name,\r\n });\r\n });\r\n }\r\n return references;\r\n}\r\n\r\n/**\r\n * Uno slot a lista porta l'indice **anche** quando contiene un solo elemento: l'id della\r\n * porta e dell'arco non deve cambiare forma quando l'utente aggiunge la seconda sorgente a\r\n * un `AppendNode`, altrimenti la selezione dell'arco salta e la posizione della porta cambia\r\n * sotto il puntatore.\r\n */\r\nfunction slotIsPositional(slot: DpeSourceSlot<DpeAnyNode>): boolean {\r\n return slot.slot === 'sources' || slot.slot === 'resultFields' || slot.slot === 'relationships';\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// Archi\r\n// ---------------------------------------------------------------------------\r\n\r\nexport interface DpeGraphNode {\r\n id: string;\r\n name: string;\r\n collection: DpeNodeCollectionName;\r\n outputPortId: string;\r\n}\r\n\r\nexport interface DpeGraphEdge {\r\n id: string;\r\n /** Nome del nodo a monte, come scritto nel documento. */\r\n fromNodeName: string;\r\n toNodeName: string;\r\n slot: string;\r\n index?: number;\r\n fromPortId: string;\r\n toPortId: string;\r\n /**\r\n * `false` quando il nome a monte non corrisponde a nessun nodo della definizione. L'arco\r\n * **si mostra comunque**: la topologia mostrata e' quella reale, e un riferimento rotto va\r\n * visto, non nascosto. La diagnostica che lo qualifica (`DPE_REF_UNKNOWN_NODE`) arriva dal\r\n * backend.\r\n */\r\n isResolved: boolean;\r\n}\r\n\r\nexport interface DpeGraph {\r\n nodes: DpeGraphNode[];\r\n edges: DpeGraphEdge[];\r\n}\r\n\r\n/**\r\n * Deriva nodi e archi dalla definizione, per riferimento di nome. Funzione pura: nessuna\r\n * lettura di stato, nessuna dipendenza da Angular, testabile senza DOM.\r\n */\r\nexport function dpeBuildGraph(definition: DpeDefinition): DpeGraph {\r\n const located = dpeAllNodes(definition);\r\n const known = new Set(located.map((entry) => entry.node.name));\r\n\r\n const nodes: DpeGraphNode[] = located.map((entry) => ({\r\n id: dpeNodeId(entry.node.name),\r\n name: entry.node.name,\r\n collection: entry.collection,\r\n outputPortId: dpeOutputPortId(entry.node.name),\r\n }));\r\n\r\n const edges: DpeGraphEdge[] = [];\r\n for (const entry of located) {\r\n for (const reference of dpeNodeSourceReferences(entry)) {\r\n if (!reference.isEdge || reference.sourceName === undefined) {\r\n continue;\r\n }\r\n edges.push({\r\n id: dpeEdgeId(reference.targetNodeName, reference.slot, reference.index),\r\n fromNodeName: reference.sourceName,\r\n toNodeName: reference.targetNodeName,\r\n slot: reference.slot,\r\n index: reference.index,\r\n fromPortId: dpeOutputPortId(reference.sourceName),\r\n toPortId: dpeInputPortId(reference.targetNodeName, reference.slot, reference.index),\r\n isResolved: known.has(reference.sourceName),\r\n });\r\n }\r\n }\r\n\r\n return { nodes, edges };\r\n}\r\n\r\n/** Le porte di ingresso di un nodo, nell'ordine in cui vanno disegnate. */\r\nexport interface DpeInputPort {\r\n id: string;\r\n slot: string;\r\n index?: number;\r\n /** Nome collegato, assente se la porta e' libera. */\r\n sourceName?: string;\r\n}\r\n\r\nexport function dpeInputPorts(located: DpeLocatedNode): DpeInputPort[] {\r\n return dpeNodeSourceReferences(located)\r\n .filter((reference) => reference.isEdge)\r\n .map((reference) => ({\r\n id: dpeInputPortId(reference.targetNodeName, reference.slot, reference.index),\r\n slot: reference.slot,\r\n index: reference.index,\r\n sourceName: reference.sourceName,\r\n }));\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// Riscrittura dei riferimenti\r\n// ---------------------------------------------------------------------------\r\n\r\n/**\r\n * Riscrive un riferimento in **uno** slot di **un** nodo. E' il mattone di `connect` e\r\n * `disconnect` dello store: passando `undefined` la porta torna libera.\r\n */\r\nexport function dpeWriteSourceReference<K extends DpeNodeCollectionName>(\r\n collection: K,\r\n node: DpeNodeCollectionMap[K],\r\n slotName: string,\r\n index: number | undefined,\r\n sourceName: string | undefined,\r\n): DpeNodeCollectionMap[K] {\r\n const slots = DPE_SOURCE_SLOTS[collection] as readonly DpeSourceSlot<DpeNodeCollectionMap[K]>[];\r\n const slot = slots.find((candidate) => candidate.slot === slotName);\r\n if (!slot) {\r\n return node;\r\n }\r\n const names = slot.read(node).slice();\r\n const position = index ?? 0;\r\n // Uno slot a lista puo' non avere ancora la posizione richiesta: la si apre, altrimenti\r\n // collegare la seconda sorgente di un Append non avrebbe nessun effetto e nessun errore.\r\n while (names.length <= position) {\r\n names.push(undefined);\r\n }\r\n names[position] = sourceName;\r\n return slot.write(node, names);\r\n}\r\n\r\n/**\r\n * **Rinominare un nodo e' un refactoring globale, non locale** (**I5**).\r\n *\r\n * Questa e' l'unica funzione che rinomina: aggiorna il `name` del nodo e **ogni** riferimento\r\n * a valle, passando dall'unico elenco di slot — `sourceName`, `sources`,\r\n * `primarySourceName`/`secondarySourceName`, i `sourceName` dei `JoinResultField` e i nomi\r\n * dentro i gruppi atomici. Se una collezione di riferimenti sfuggisse, il documento\r\n * diventerebbe silenziosamente incoerente: e' il difetto che questa funzione esiste per non\r\n * avere.\r\n *\r\n * Cosa **non** fa, di proposito: non rinomina gli **alias di colonna** (sono un altro spazio\r\n * di nomi) e non decide se il nuovo nome sia lecito o gia' usato — l'unicita' e' **I1**, la\r\n * verifica il backend.\r\n *\r\n * Il confronto e' **esatto**, non case-insensitive: `domain-model.md` dichiara il confronto\r\n * `OrdinalIgnoreCase` per gli **alias di colonna** e non dice nulla sui nomi dei nodi.\r\n * Rinominare anche le occorrenze con capitalizzazione diversa significherebbe, se il backend\r\n * risolvesse i nomi in modo esatto, riscrivere il riferimento a un nodo **diverso**.\r\n */\r\nexport function dpeRenameNode(\r\n definition: DpeDefinition,\r\n oldName: string,\r\n newName: string,\r\n): DpeDefinition {\r\n if (oldName === newName) {\r\n return definition;\r\n }\r\n\r\n const next: DpeDefinition = { ...definition };\r\n\r\n for (const collection of DPE_NODE_COLLECTIONS) {\r\n const nodes = definition[collection] as DpeAnyNode[] | undefined;\r\n if (!nodes || nodes.length === 0) {\r\n continue;\r\n }\r\n const slots = DPE_SOURCE_SLOTS[collection] as readonly DpeSourceSlot<DpeAnyNode>[];\r\n const rewritten = nodes.map((node) => {\r\n let result = node;\r\n for (const slot of slots) {\r\n const names = slot.read(result);\r\n if (!names.some((name) => name === oldName)) {\r\n continue;\r\n }\r\n result = slot.write(\r\n result,\r\n names.map((name) => (name === oldName ? newName : name)),\r\n );\r\n }\r\n if (result.name === oldName) {\r\n result = { ...result, name: newName };\r\n }\r\n return result;\r\n });\r\n // L'assegnazione per nome di collezione non e' esprimibile in modo tipato senza un cast:\r\n // la mappa collezione -> tipo di nodo esiste, ma TypeScript non correla la chiave con il\r\n // valore dentro un ciclo. Il cast e' confinato a questa riga.\r\n (next as unknown as Record<string, unknown>)[collection] = rewritten;\r\n }\r\n\r\n return next;\r\n}\r\n\r\n/** Quante occorrenze toccherebbe una rinomina: serve a dirlo prima, non dopo. */\r\nexport function dpeCountReferences(definition: DpeDefinition, nodeName: string): number {\r\n let count = 0;\r\n for (const located of dpeAllNodes(definition)) {\r\n for (const reference of dpeNodeSourceReferences(located)) {\r\n if (reference.sourceName === nodeName) {\r\n count += 1;\r\n }\r\n }\r\n }\r\n return count;\r\n}\r\n","/**\r\n * Il controllo di un valore che arriva da fuori e che si vuole usare come nome di collezione.\r\n *\r\n * **Non sta in `public-api.ts`, di proposito.** `public-api.ts` riesporta per intero sia\r\n * `model/dpe-definition.model` sia `core/dpe-node-catalog`, quindi un `export` in uno dei due\r\n * sarebbe API pubblica: questo file esiste per poter servire due siti della libreria senza\r\n * diventarlo. Chi lo volesse esportare deve deciderlo come API, non trovarcelo.\r\n *\r\n * I due confini che lo usano convertono un dato che il tipo non garantisce: il `fData` di un\r\n * rilascio sul canvas (`unknown` per @foblex/flow) e il primo segmento del percorso di una\r\n * diagnostica, che arriva dal backend e nomina anche array della definizione che collezioni di\r\n * nodi non sono — `parameters` su tutti. Dopo questo controllo `dpeNodeCategory` puo' trattare\r\n * un valore fuori dall'unione come un errore di programmazione, e lanciare.\r\n */\r\n\r\nimport { DPE_NODE_COLLECTIONS, DpeNodeCollectionName } from '../model/dpe-definition.model';\r\n\r\n/** Lo stesso elenco del modello, non una copia: una collezione nuova vale qui senza toccarlo. */\r\nconst NODE_COLLECTIONS: ReadonlySet<unknown> = new Set<unknown>(DPE_NODE_COLLECTIONS);\r\n\r\n/** Vero solo per un membro di `DPE_NODE_COLLECTIONS`, a confronto esatto (maiuscole comprese). */\r\nexport function dpeIsNodeCollectionName(value: unknown): value is DpeNodeCollectionName {\r\n return NODE_COLLECTIONS.has(value);\r\n}\r\n","/**\r\n * Utility pure sulle diagnostiche **ricevute** dal backend: raggruppamento per nodo, gravita'\r\n * peggiore, e la distinzione fra \"da correggere\" e \"non ancora compilabile\".\r\n *\r\n * Qui non nasce nessuna diagnostica e non si giudica nessuna definizione: si legge cio' che il\r\n * backend ha detto e si decide **come renderlo**. La sola informazione interpretata e' il\r\n * `DiagnosticPath`, che il contratto pubblica in **due forme** (con nome del nodo dalla\r\n * validazione, con solo indice dalla deserializzazione): chi legge deve accettarle entrambe,\r\n * altrimenti le diagnostiche del caricamento di un file non sono navigabili.\r\n */\r\n\r\nimport { Diagnostic, DiagnosticSeverity, DPE_NODE_NOT_COMPILABLE } from '../model/dpe-diagnostics.model';\r\nimport { DpeDefinition } from '../model/dpe-definition.model';\r\nimport { dpeAllNodes } from './dpe-graph';\r\nimport { dpeIsNodeCollectionName } from './dpe-node-collection-guard';\r\n\r\n/**\r\n * Il nodo a cui una diagnostica si riferisce, se ricavabile.\r\n *\r\n * Tre strade, nell'ordine: il nome nel primo segmento del percorso; l'argomento canonico\r\n * `node`; l'indice del primo segmento risolto nella collezione corrispondente della\r\n * definizione — che e' il caso della deserializzazione, dove i nomi non sono ancora noti.\r\n * Senza la terza, un `filters[0].sourceName` resterebbe una riga non cliccabile.\r\n */\r\nexport function dpeDiagnosticNodeName(\r\n diagnostic: Diagnostic,\r\n definition?: DpeDefinition,\r\n): string | undefined {\r\n const first = diagnostic.path?.segments?.[0];\r\n if (first?.name) {\r\n return first.name;\r\n }\r\n const fromArguments = diagnostic.arguments?.['node'];\r\n if (fromArguments) {\r\n return fromArguments;\r\n }\r\n if (!definition || first?.index === undefined) {\r\n return undefined;\r\n }\r\n /*\r\n * Il segmento arriva dal backend e puo' nominare un array della definizione che non e' una\r\n * collezione di nodi: `parameters[i]` ha voci con un `name` e passava `Array.isArray`, quindi\r\n * il nome di un parametro tornava come nodo — riga cliccabile verso un nodo inesistente, e\r\n * diagnostica sparita da quelle della definizione. Il controllo sostituisce `Array.isArray`,\r\n * che proteggeva solo dalle chiavi non array (`name`, `dialect`).\r\n */\r\n if (!dpeIsNodeCollectionName(first.collection)) {\r\n return undefined;\r\n }\r\n return definition[first.collection]?.[first.index]?.name;\r\n}\r\n\r\n/** Il campo in causa, per evidenziarlo nell'ispettore. */\r\nexport function dpeDiagnosticField(diagnostic: Diagnostic): string | undefined {\r\n return diagnostic.path?.field;\r\n}\r\n\r\n/**\r\n * La diagnostica riguarda **quella riga** di una collezione dentro il nodo, e quel campo.\r\n *\r\n * Serve dove un nodo ha piu' righe con lo stesso nome di campo — il caso reale sono le colonne\r\n * calcolate di una trasformazione, che hanno tutte un `expression`. Filtrare per solo nome di\r\n * campo, come fa `issuesOn`, mostrerebbe l'errore della prima formula sotto la terza: il rilievo\r\n * arriva accanto a un testo che non lo ha prodotto, ed e' peggio di non mostrarlo.\r\n *\r\n * Il confronto e' sul segmento del percorso che nomina la collezione, ovunque stia: `contracts.md`\r\n * §1 pubblica il percorso in **due** forme (col nome del nodo dalla validazione, col solo indice\r\n * dalla deserializzazione), e in entrambe il segmento della riga porta l'indice.\r\n */\r\nexport function dpeDiagnosticTargetsItem(\r\n diagnostic: Diagnostic,\r\n collection: string,\r\n index: number,\r\n field: string,\r\n): boolean {\r\n if (dpeDiagnosticField(diagnostic) !== field) {\r\n return false;\r\n }\r\n return (diagnostic.path?.segments ?? []).some(\r\n (segment) => segment.collection === collection && segment.index === index,\r\n );\r\n}\r\n\r\n/**\r\n * `true` per l'unico codice che significa \"hai fatto bene, siamo noi\" (**I10**, ADR 0005):\r\n * `Forecast`, `CustomNode`, costrutto non supportato dal dialetto scelto. Non va mescolato\r\n * con i `DPE_MODEL_*`, che l'utente deve correggere.\r\n */\r\nexport function dpeIsNotCompilable(diagnostic: Diagnostic): boolean {\r\n return diagnostic.code === DPE_NODE_NOT_COMPILABLE;\r\n}\r\n\r\n/**\r\n * `true` quando la diagnostica riguarda le **capacita' del dialetto** e non la definizione in\r\n * se': l'interfaccia deve dire «questo dialetto non lo supporta», perche' lo stesso documento\r\n * su un altro dialetto sarebbe compilabile.\r\n */\r\nexport function dpeIsDialectDiagnostic(diagnostic: Diagnostic): boolean {\r\n return diagnostic.code.startsWith('DPE_DIALECT_');\r\n}\r\n\r\nexport interface DpeNodeDiagnostics {\r\n /** Diagnostiche ancorate al nodo, nell'ordine ricevuto (**I6**: l'elenco e' esaustivo). */\r\n diagnostics: Diagnostic[];\r\n /** La gravita' peggiore fra quelle presenti. */\r\n severity?: DiagnosticSeverity;\r\n /**\r\n * Il nodo e' **valido ma non compilabile**: stato distinto da \"non valido\", e l'unico per\r\n * cui la UI non deve chiedere all'utente di correggere niente.\r\n */\r\n isNotCompilable: boolean;\r\n}\r\n\r\nconst SEVERITY_RANK: Readonly<Record<DiagnosticSeverity, number>> = {\r\n Info: 1,\r\n Warning: 2,\r\n Error: 3,\r\n};\r\n\r\nexport function dpeWorstSeverity(diagnostics: readonly Diagnostic[]): DiagnosticSeverity | undefined {\r\n let worst: DiagnosticSeverity | undefined;\r\n for (const diagnostic of diagnostics) {\r\n if (!worst || SEVERITY_RANK[diagnostic.severity] > SEVERITY_RANK[worst]) {\r\n worst = diagnostic.severity;\r\n }\r\n }\r\n return worst;\r\n}\r\n\r\n/** Raggruppa per nodo. Le diagnostiche non ancorabili a un nodo non si perdono: vedi sotto. */\r\nexport function dpeGroupDiagnosticsByNode(\r\n diagnostics: readonly Diagnostic[],\r\n definition?: DpeDefinition,\r\n): Map<string, DpeNodeDiagnostics> {\r\n const grouped = new Map<string, DpeNodeDiagnostics>();\r\n for (const diagnostic of diagnostics) {\r\n const nodeName = dpeDiagnosticNodeName(diagnostic, definition);\r\n if (nodeName === undefined) {\r\n continue;\r\n }\r\n const entry = grouped.get(nodeName) ?? { diagnostics: [], isNotCompilable: false };\r\n entry.diagnostics.push(diagnostic);\r\n entry.isNotCompilable = entry.isNotCompilable || dpeIsNotCompilable(diagnostic);\r\n entry.severity = dpeWorstSeverity(entry.diagnostics);\r\n grouped.set(nodeName, entry);\r\n }\r\n return grouped;\r\n}\r\n\r\n/**\r\n * Le diagnostiche che **non** appartengono a nessun nodo (radice, parametri, sequenza dei\r\n * writeback, grafo intero). Esistono come funzione a se' perche' il pannello deve mostrarle:\r\n * un `DPE_GRAPH_NO_WRITEBACK` che scompare perche' non ha un nodo a cui attaccarsi sarebbe la\r\n * diagnostica piu' importante di tutte a non arrivare all'utente.\r\n */\r\nexport function dpeDefinitionLevelDiagnostics(\r\n diagnostics: readonly Diagnostic[],\r\n definition?: DpeDefinition,\r\n): Diagnostic[] {\r\n return diagnostics.filter(\r\n (diagnostic) => dpeDiagnosticNodeName(diagnostic, definition) === undefined,\r\n );\r\n}\r\n\r\n/** Testo leggibile del percorso, per la riga del pannello. */\r\nexport function dpeDiagnosticLocationLabel(\r\n diagnostic: Diagnostic,\r\n definition?: DpeDefinition,\r\n): string {\r\n const node = dpeDiagnosticNodeName(diagnostic, definition);\r\n const segments = diagnostic.path?.segments ?? [];\r\n const tail = segments\r\n .slice(1)\r\n .map((segment) => {\r\n const index = segment.index === undefined ? '' : `[${segment.index}]`;\r\n return `${segment.collection ?? ''}${segment.name ? `/${segment.name}` : index}`;\r\n })\r\n .filter((part) => part.length > 0);\r\n const field = diagnostic.path?.field;\r\n return [node ?? 'definizione', ...tail, field].filter(Boolean).join(' › ');\r\n}\r\n\r\n/** I nomi dei nodi presenti nella definizione: serve a chi naviga dal pannello al canvas. */\r\nexport function dpeKnownNodeNames(definition: DpeDefinition): Set<string> {\r\n return new Set(dpeAllNodes(definition).map((located) => located.node.name));\r\n}\r\n","/**\r\n * Le coordinate di un nodo sul canvas — ADR 0013.\r\n *\r\n * Vivono in `extensionData` sotto la chiave namespaced `dpe:layout`, forma `{x, y}`. Il\r\n * backend le conserva senza interpretarle (**I7**), la validazione non le guarda, il\r\n * compilatore non le vede: l'unico livello che gli da' significato e' l'editor.\r\n *\r\n * Due regole dell'ADR sono implementate qui, e sono le sole che contano:\r\n *\r\n * - le coordinate sono **opzionali**: una definizione scritta a mano non le ha e resta\r\n * valida; in loro assenza si esegue dagre;\r\n * - un `dpe:layout` **malformato o parziale si ignora**. Un documento arrivato da un'altra\r\n * versione non deve poter rompere il canvas, e \"meta' coordinata\" non e' una posizione:\r\n * accettarla metterebbe il nodo a `x` giusta e `y` zero, cioe' in un posto che nessuno ha\r\n * scelto.\r\n */\r\n\r\nimport { DpeAnyNode, DpeExtensionData } from '../model/dpe-definition.model';\r\n\r\n/** La chiave e' namespaced perche' `extensionData` e' anche dove atterrano i campi futuri. */\r\nexport const DPE_LAYOUT_KEY = 'dpe:layout';\r\n\r\nexport interface DpeNodePosition {\r\n x: number;\r\n y: number;\r\n}\r\n\r\n/** Legge la posizione, oppure `undefined` se assente o non utilizzabile. */\r\nexport function dpeReadNodePosition(node: DpeAnyNode): DpeNodePosition | undefined {\r\n const raw = node.extensionData?.[DPE_LAYOUT_KEY];\r\n if (raw === null || typeof raw !== 'object') {\r\n return undefined;\r\n }\r\n const candidate = raw as { x?: unknown; y?: unknown };\r\n // `Number.isFinite` e non `typeof === 'number'`: `NaN` e `Infinity` sono numeri per\r\n // TypeScript e coordinate impossibili per il canvas, dove finirebbero in una trasform CSS\r\n // invalida — il nodo semplicemente non si vede, senza nessun errore.\r\n if (!Number.isFinite(candidate.x) || !Number.isFinite(candidate.y)) {\r\n return undefined;\r\n }\r\n return { x: candidate.x as number, y: candidate.y as number };\r\n}\r\n\r\n/** Scrive la posizione producendo un nuovo nodo: il documento resta immutabile. */\r\nexport function dpeWriteNodePosition<TNode extends DpeAnyNode>(\r\n node: TNode,\r\n position: DpeNodePosition,\r\n): TNode {\r\n const extensionData: DpeExtensionData = {\r\n ...(node.extensionData ?? {}),\r\n [DPE_LAYOUT_KEY]: { x: Math.round(position.x), y: Math.round(position.y) },\r\n };\r\n return { ...node, extensionData };\r\n}\r\n\r\n/** Vero quando **tutti** i nodi hanno una posizione: se no, il canvas rifa' il layout. */\r\nexport function dpeHasCompleteLayout(nodes: readonly DpeAnyNode[]): boolean {\r\n return nodes.length > 0 && nodes.every((node) => dpeReadNodePosition(node) !== undefined);\r\n}\r\n","/**\r\n * Catalogo delle categorie di nodo: etichetta italiana, collezione JSON di appartenenza,\r\n * numero di sorgenti ammesse.\r\n *\r\n * **Descrittivo, non normativo.** Serve alla palette e alle etichette dell'interfaccia. Non\r\n * e' validazione: che un `AppendNode` con una sola sorgente sia sbagliato lo dice il backend,\r\n * non questo file, e i due non devono mai diventare due fonti di verita' in disaccordo. I\r\n * conteggi qui sotto servono a decidere **quante porte disegnare**, che e' una domanda di\r\n * rendering.\r\n */\r\n\r\nimport { DPE_NODE_COLLECTIONS, DpeNodeCollectionName } from '../model/dpe-definition.model';\r\n\r\nexport interface DpeNodeCategory {\r\n /** Collezione JSON della radice: e' anche la chiave con cui si legge il documento. */\r\n collection: DpeNodeCollectionName;\r\n /** Etichetta breve, per la palette e per l'intestazione della card sul canvas. */\r\n label: string;\r\n /** Una riga di spiegazione, per il tooltip della palette. */\r\n description: string;\r\n /** Numero minimo di sorgenti che il nodo dichiara (0 per i punti d'ingresso). */\r\n minSources: number;\r\n /** Numero massimo, `undefined` = illimitato (`Append`, `CustomNode`). */\r\n maxSources?: number;\r\n /** Vero per i punti d'ingresso del grafo. */\r\n isSource: boolean;\r\n /** Vero per i sink: producono l'artefatto, non uno schema. */\r\n isSink: boolean;\r\n /**\r\n * Vero dove il backend rifiuta la compilazione pur accettando il modello (**I10**): la UI\r\n * deve poter avvisare **prima** che l'utente costruisca la pipeline intorno a un nodo che\r\n * non arrivera' all'SQL. Non e' una regola di dominio riprodotta: e' l'informazione che\r\n * `docs/domain-model.md` dichiara esplicitamente per queste due categorie, e la\r\n * diagnostica autorevole resta `DPE_NODE_NOT_COMPILABLE` del backend.\r\n */\r\n isNotCompilableInV1: boolean;\r\n}\r\n\r\n/**\r\n * Cio' che il catalogo scrive di una categoria: tutto tranne la collezione, che e' la **chiave**\r\n * della voce. Tenerla anche dentro la voce sarebbe una seconda copia dello stesso fatto, e la\r\n * seconda copia si scosta (ADR 0018): qui non si puo' nemmeno scrivere, perche' il tipo non la\r\n * ammette e il controllo delle proprieta' in eccesso la rifiuta.\r\n */\r\ntype DpeNodeCategoryFacts = Omit<DpeNodeCategory, 'collection'>;\r\n\r\n/**\r\n * Il catalogo, **una voce per collezione**. E' un `Record` sull'unione e non un elenco di voci\r\n * sciolte: il compilatore pretende una voce per **ogni** membro di `DpeNodeCollectionName`, e una\r\n * collezione aggiunta al modello senza voce qui ferma la build («Property '...' is missing»).\r\n * Con l'elenco di prima la stessa omissione compilava e finiva nel ripiego di `dpeNodeCategory`:\r\n * un pozzo dimenticato vi diventa un nodo di mezzo, con il conteggio colonne sulla card e\r\n * «Nessuna colonna in uscita.» nella dialog — due rese sbagliate che non dicono niente.\r\n *\r\n * L'ordine delle chiavi qui non conta e non va letto: l'ordine e' quello di `DPE_NODE_COLLECTIONS`.\r\n */\r\nconst CATEGORY_FACTS: { readonly [K in DpeNodeCollectionName]: DpeNodeCategoryFacts } = {\r\n dataSources: {\r\n label: 'Sorgente',\r\n description: 'Punto d’ingresso: tabella del provider di metadati, CSV o dataset esterno.',\r\n minSources: 0,\r\n maxSources: 0,\r\n isSource: true,\r\n isSink: false,\r\n isNotCompilableInV1: false,\r\n },\r\n filters: {\r\n label: 'Filtro',\r\n description: 'Seleziona le righe con criteri combinati, oppure con un filtro dinamico da parametro.',\r\n minSources: 1,\r\n maxSources: 1,\r\n isSource: false,\r\n isSink: false,\r\n isNotCompilableInV1: false,\r\n },\r\n joins: {\r\n label: 'Join',\r\n description: 'Unisce due sorgenti su una o piu’ chiavi; lo schema di uscita e’ esplicito.',\r\n minSources: 2,\r\n maxSources: 2,\r\n isSource: false,\r\n isSink: false,\r\n isNotCompilableInV1: false,\r\n },\r\n aggregates: {\r\n label: 'Aggregazione',\r\n description: 'Raggruppa e calcola funzioni di aggregazione; senza raggruppamento produce una riga.',\r\n minSources: 1,\r\n maxSources: 1,\r\n isSource: false,\r\n isSink: false,\r\n isNotCompilableInV1: false,\r\n },\r\n transforms: {\r\n label: 'Trasformazione',\r\n description: 'Aggiunge colonne calcolate, ne elimina, oppure calcola valori relativi a una partizione.',\r\n minSources: 1,\r\n maxSources: 1,\r\n isSource: false,\r\n isSink: false,\r\n isNotCompilableInV1: false,\r\n },\r\n appends: {\r\n label: 'Unione',\r\n description: 'Impila N sorgenti; con schemi disgiunti le colonne mancanti diventano nulle.',\r\n minSources: 2,\r\n isSource: false,\r\n isSink: false,\r\n isNotCompilableInV1: false,\r\n },\r\n hierarchyPaths: {\r\n label: 'Percorso gerarchico',\r\n description: 'Risale una gerarchia padre/figlio e produce la colonna di percorso; richiede CTE ricorsive.',\r\n minSources: 1,\r\n maxSources: 1,\r\n isSource: false,\r\n isSink: false,\r\n isNotCompilableInV1: false,\r\n },\r\n forecasts: {\r\n label: 'Previsione',\r\n description: 'Modellata per intero ma non traducibile in SQL nella versione 1.',\r\n minSources: 1,\r\n maxSources: 1,\r\n isSource: false,\r\n isSink: false,\r\n isNotCompilableInV1: true,\r\n },\r\n customNodes: {\r\n label: 'Nodo personalizzato',\r\n description: 'Estensione esterna con schema di uscita dichiarato; non traducibile in SQL nella versione 1.',\r\n minSources: 1,\r\n isSource: false,\r\n isSink: false,\r\n isNotCompilableInV1: true,\r\n },\r\n writebacks: {\r\n label: 'Scrittura',\r\n description: 'Sink: produce l’istruzione SQL per l’host, oppure un file JSON o CSV.',\r\n minSources: 1,\r\n maxSources: 1,\r\n isSource: false,\r\n isSink: true,\r\n isNotCompilableInV1: false,\r\n },\r\n atomicWritebacks: {\r\n label: 'Gruppo atomico',\r\n description: 'Dichiara che alcune scritture vanno applicate dall’host nella stessa transazione.',\r\n minSources: 0,\r\n isSource: false,\r\n isSink: true,\r\n isNotCompilableInV1: false,\r\n },\r\n};\r\n\r\n/**\r\n * Le categorie nell'ordine di `DPE_NODE_COLLECTIONS`, che e' l'ordine della palette. **Derivate,\r\n * non riscritte**: l'ordine vive in un posto solo — il modello, dove un controllo di tipo lo tiene\r\n * totale e senza ripetizioni — e la collezione di ogni categoria e' la stessa variabile con cui se\r\n * ne legge la voce, quindi chiave e campo non hanno modo di divergere.\r\n */\r\nexport const DPE_NODE_CATEGORIES: readonly DpeNodeCategory[] = DPE_NODE_COLLECTIONS.map(\r\n (collection) => ({ collection, ...CATEGORY_FACTS[collection] }),\r\n);\r\n\r\n/** Le stesse istanze dell'elenco: la palette e le card leggono lo stesso oggetto. */\r\nconst BY_COLLECTION = new Map<DpeNodeCollectionName, DpeNodeCategory>(\r\n DPE_NODE_CATEGORIES.map((category) => [category.collection, category]),\r\n);\r\n\r\nexport function dpeNodeCategory(collection: DpeNodeCollectionName): DpeNodeCategory {\r\n const category = BY_COLLECTION.get(collection);\r\n if (!category) {\r\n /*\r\n * Nessun membro di `DpeNodeCollectionName` arriva qui: `CATEGORY_FACTS` e' un `Record`\r\n * sull'unione e `DPE_NODE_COLLECTIONS` e' controllato totale, quindi una voce che manca e' un\r\n * errore di build, non un caso di esecuzione. Ci arriverebbe soltanto un valore che il tipo\r\n * dichiara collezione e che non lo e', e i confini che convertono un dato esterno — il `fData`\r\n * di un rilascio sul canvas, il segmento di percorso di una diagnostica — ora lo controllano\r\n * prima (`dpe-node-collection-guard.ts`). Quindi arrivarci e' un errore di programmazione, e\r\n * si lancia nominando il valore.\r\n *\r\n * Prima qui c'era un ripiego neutro, giustificato dal confine non controllato: rendeva un\r\n * pozzo come nodo di mezzo, in silenzio. Tolto il confine scoperto, si toglie la tolleranza che\r\n * lo assorbiva.\r\n */\r\n throw new Error(`dpeNodeCategory: «${String(collection)}» non e’ una collezione di nodi.`);\r\n }\r\n return category;\r\n}\r\n\r\n/** Nomi dei dialetti per il selettore. La libreria conosce **solo** il nome (ADR 0009). */\r\nexport const DPE_DIALECT_LABELS: Readonly<Record<string, string>> = {\r\n Sqlite: 'SQLite',\r\n SqlServer: 'SQL Server',\r\n PostgreSql: 'PostgreSQL',\r\n Oracle: 'Oracle',\r\n};\r\n","/**\r\n * Creazione di un nodo nuovo: nome candidato e valori iniziali per categoria.\r\n *\r\n * Non e' validazione e non e' dominio. Serve a una cosa sola: un nodo appena rilasciato sul\r\n * canvas deve essere **rappresentabile** e distinguibile, cioe' avere un nome e un'etichetta.\r\n * Tutto il resto lo compila l'utente nell'ispettore, e cosa sia obbligatorio lo dice il\r\n * backend.\r\n *\r\n * Il nome candidato e' unico rispetto ai nomi gia' presenti con confronto **esatto**\r\n * (`contracts.md` §2: unicita' verificata `OrdinalIgnoreCase`, risoluzione `Ordinal`). Qui il\r\n * confronto e' insensibile al caso di proposito: proporre `Filtro2` accanto a un `filtro2`\r\n * esistente creerebbe un `DPE_MODEL_NAME_DUPLICATE` a nome dell'editor, non dell'utente.\r\n */\r\n\r\nimport {\r\n DpeAnyNode,\r\n DpeDefinition,\r\n DpeNodeCollectionMap,\r\n DpeNodeCollectionName,\r\n} from '../model/dpe-definition.model';\r\nimport { dpeAllNodes } from './dpe-graph';\r\nimport { dpeNodeCategory } from './dpe-node-catalog';\r\n\r\n/** Radice del nome proposto per categoria: identificatori, quindi senza spazi ne' accenti. */\r\nconst NAME_STEM: Readonly<Record<DpeNodeCollectionName, string>> = {\r\n dataSources: 'Sorgente',\r\n filters: 'Filtro',\r\n joins: 'Join',\r\n aggregates: 'Aggregazione',\r\n transforms: 'Trasformazione',\r\n appends: 'Unione',\r\n hierarchyPaths: 'Gerarchia',\r\n forecasts: 'Previsione',\r\n customNodes: 'NodoCustom',\r\n writebacks: 'Scrittura',\r\n atomicWritebacks: 'GruppoAtomico',\r\n};\r\n\r\n/** Il primo nome libero della forma `<radice><n>`. */\r\nexport function dpeProposeNodeName(\r\n definition: DpeDefinition,\r\n collection: DpeNodeCollectionName,\r\n): string {\r\n const used = new Set(dpeAllNodes(definition).map((located) => located.node.name.toLowerCase()));\r\n const stem = NAME_STEM[collection];\r\n let index = 1;\r\n while (used.has(`${stem}${index}`.toLowerCase())) {\r\n index += 1;\r\n }\r\n return `${stem}${index}`;\r\n}\r\n\r\n/**\r\n * Il nodo iniziale di una categoria. I campi obbligatori senza un valore sensato restano\r\n * **assenti**, non riempiti con un segnaposto: un `tableName` finto passerebbe la forma e\r\n * fallirebbe sul provider di metadati con un errore che l'utente non ha causato.\r\n */\r\nexport function dpeCreateNode<K extends DpeNodeCollectionName>(\r\n definition: DpeDefinition,\r\n collection: K,\r\n): DpeNodeCollectionMap[K] {\r\n const name = dpeProposeNodeName(definition, collection);\r\n const label = `${dpeNodeCategory(collection).label} ${name.replace(/^\\D+/, '')}`;\r\n const base = { name, label };\r\n\r\n switch (collection) {\r\n case 'dataSources':\r\n return { ...base, kind: 'Table', fields: [] } as unknown as DpeNodeCollectionMap[K];\r\n case 'joins':\r\n return { ...base, kind: 'Inner', keys: [], fields: [] } as unknown as DpeNodeCollectionMap[K];\r\n case 'aggregates':\r\n return { ...base, fields: [] } as unknown as DpeNodeCollectionMap[K];\r\n case 'transforms':\r\n return { ...base, kind: 'Expression', expressionFields: [] } as unknown as DpeNodeCollectionMap[K];\r\n case 'appends':\r\n // Due posizioni vuote: un'unione nasce con due ingressi da collegare, ed e' anche cio'\r\n // che fa comparire due porte sul canvas invece di nessuna.\r\n return { ...base, sources: ['', ''] } as unknown as DpeNodeCollectionMap[K];\r\n case 'customNodes':\r\n return { ...base, sources: [''], extensionName: '', outputFields: [] } as unknown as DpeNodeCollectionMap[K];\r\n case 'forecasts':\r\n return { ...base, aggregationFields: [] } as unknown as DpeNodeCollectionMap[K];\r\n case 'writebacks':\r\n return {\r\n ...base,\r\n target: 'Database',\r\n operation: 'Insert',\r\n sequence: dpeProposeWritebackSequence(definition),\r\n fields: [],\r\n } as unknown as DpeNodeCollectionMap[K];\r\n case 'atomicWritebacks':\r\n return {\r\n ...base,\r\n sequence: dpeProposeWritebackSequence(definition),\r\n relationships: [],\r\n } as unknown as DpeNodeCollectionMap[K];\r\n default:\r\n return base as unknown as DpeNodeCollectionMap[K];\r\n }\r\n}\r\n\r\n/**\r\n * La sequenza dei sink e' univoca fra writeback e gruppi atomici: si propone la prima libera.\r\n * L'unicita' la verifica il backend (`DPE_WRITEBACK_SEQUENCE_DUPLICATE`) — questo e' solo un\r\n * valore iniziale che non collide, non un controllo.\r\n */\r\nexport function dpeProposeWritebackSequence(definition: DpeDefinition): number {\r\n const used = new Set<number>();\r\n for (const node of [...(definition.writebacks ?? []), ...(definition.atomicWritebacks ?? [])]) {\r\n used.add(node.sequence);\r\n }\r\n let sequence = 1;\r\n while (used.has(sequence)) {\r\n sequence += 1;\r\n }\r\n return sequence;\r\n}\r\n\r\n/** Etichetta di sintesi mostrata sulla card del nodo, sotto il nome. */\r\nexport function dpeNodeSubtitle(collection: DpeNodeCollectionName, node: DpeAnyNode): string | null {\r\n const record = node as unknown as Record<string, unknown>;\r\n switch (collection) {\r\n case 'dataSources':\r\n return (record['tableName'] as string) || (record['externalDatasetName'] as string) || null;\r\n case 'joins':\r\n return (record['kind'] as string) || null;\r\n case 'transforms':\r\n return (record['kind'] as string) || null;\r\n case 'aggregates': {\r\n const fields = (record['fields'] as unknown[] | undefined)?.length ?? 0;\r\n return fields ? `${fields} ${fields === 1 ? 'funzione' : 'funzioni'}` : null;\r\n }\r\n case 'filters':\r\n return record['isDynamic'] === true ? 'dinamico' : (record['condition'] as string) || null;\r\n case 'writebacks': {\r\n const target = record['target'] as string | undefined;\r\n const table = record['tableName'] as string | undefined;\r\n return [target, table].filter(Boolean).join(' · ') || null;\r\n }\r\n case 'atomicWritebacks': {\r\n const relationships = (record['relationships'] as unknown[] | undefined)?.length ?? 0;\r\n return relationships\r\n ? `${relationships} ${relationships === 1 ? 'relazione' : 'relazioni'}`\r\n : null;\r\n }\r\n default:\r\n return null;\r\n }\r\n}\r\n","/**\r\n * Lo stato dell'editor: la definizione in memoria e' **la fonte di verita'**.\r\n *\r\n * `@Injectable()` **senza `providedIn`**: e' un provider del componente radice dell'editor,\r\n * non della root. Due editor sulla stessa pagina devono poter editare due definizioni\r\n * indipendenti, e con un servizio in root ne condividerebbero una — compresi undo/redo e\r\n * selezione.\r\n *\r\n * Stato **immutabile e unidirezionale**: ogni mutazione produce un nuovo valore della\r\n * definizione, nessuno muta l'oggetto in luogo. Da li' viene gratis l'undo/redo (istantanee\r\n * JSON), che non e' una funzionalita' aggiunta dopo ma una conseguenza.\r\n *\r\n * Cosa **non** fa questo store, e non e' una dimenticanza: non valida, non calcola schemi,\r\n * non risolve riferimenti, non sa cosa un dialetto supporti. Diagnostiche, schemi, ordine di\r\n * esecuzione e sequenza dei writeback **arrivano dal backend** e qui si depositano\r\n * (`applyValidateResult`). L'unica logica presente e' strutturale: dove vivono i riferimenti\r\n * e come si riscrivono, che sta in `dpe-graph.ts`.\r\n */\r\n\r\nimport { Injectable, computed, signal } from '@angular/core';\r\n\r\nimport {\r\n DPE_NODE_COLLECTIONS,\r\n DpeAnyNode,\r\n DpeDefinition,\r\n DpeNodeCollectionMap,\r\n DpeNodeCollectionName,\r\n SqlDialectKind,\r\n} from '../model/dpe-definition.model';\r\nimport { Diagnostic } from '../model/dpe-diagnostics.model';\r\nimport {\r\n DpeCompileResult,\r\n DpeNodeSchemaInfo,\r\n DpeValidateResult,\r\n DpeWritebackStep,\r\n} from '../model/dpe-api.model';\r\nimport {\r\n DpeGraph,\r\n dpeAllNodes,\r\n dpeBuildGraph,\r\n dpeCountReferences,\r\n dpeFindNode,\r\n dpeRenameNode,\r\n dpeWriteSourceReference,\r\n} from './dpe-graph';\r\nimport {\r\n DpeNodeDiagnostics,\r\n dpeDefinitionLevelDiagnostics,\r\n dpeGroupDiagnosticsByNode,\r\n} from './dpe-diagnostics.util';\r\nimport { DpeNodePosition, dpeReadNodePosition, dpeWriteNodePosition } from './dpe-node-layout';\r\nimport { dpeCreateNode } from './dpe-node-factory';\r\n\r\n/** Profondita' della cronologia: oltre, le istantanee piu' vecchie si buttano. */\r\nconst HISTORY_LIMIT = 100;\r\n\r\n/**\r\n * Una definizione vuota ma **leggibile**: `formatVersion` e `dialect` non sono opzionali.\r\n *\r\n * **Serve a `createDefinition`, non a montare l'editor.** E' il documento minimo che si manda al\r\n * backend per far nascere una definizione nel catalogo (ADR 0019), e da li' in poi quella che si\r\n * edita e' la definizione **caricata con `load(nome)`**.\r\n *\r\n * Montare l'editor su un documento costruito qui, o su uno costruito a mano senza nome, produce due\r\n * sintomi che sembrano scollegati e sono la stessa cosa: la validazione risponde\r\n * `DPE_MODEL_REQUIRED_MISSING` su `name` e `label` — per il backend una stringa vuota e un campo\r\n * assente sono lo stesso caso — e il salvataggio non ha una chiave per la rotta\r\n * `PUT /definitions/{name}`. L'editor lo dichiara in cima con un banner e disabilita «Salva», ma la\r\n * correzione e' a monte: creare dal catalogo e caricare.\r\n */\r\nexport function dpeEmptyDefinition(\r\n name: string,\r\n label: string,\r\n dialect: SqlDialectKind,\r\n): DpeDefinition {\r\n return { formatVersion: 1, name, label, dialect };\r\n}\r\n\r\n@Injectable()\r\nexport class DpeDocumentStore {\r\n private readonly _definition = signal<DpeDefinition>(dpeEmptyDefinition('', '', 'Sqlite'));\r\n private readonly _validateResult = signal<DpeValidateResult | undefined>(undefined);\r\n private readonly _compileResult = signal<DpeCompileResult | undefined>(undefined);\r\n /**\r\n * I nodi selezionati, in ordine di gesto. E' una **lista** e non un nome singolo perche' il\r\n * canvas sa selezionare piu' nodi (riquadro, `Ctrl+A`) e trascinarli insieme: tenere qui un solo\r\n * nome costringerebbe la libreria a conservare gli altri, cioe' a essere di nuovo autorevole\r\n * sulla selezione.\r\n */\r\n private readonly _selectedNodeNames = signal<readonly string[]>([]);\r\n /**\r\n * La dialog di modifica del nodo e' aperta. **Un booleano**, non un secondo nome di nodo: il\r\n * nodo su cui si sta agendo resta `selectedNodeName()`, e due registrazioni dello stesso fatto\r\n * sono esattamente cio' che questo store dichiara di non voler avere (ci ha gia' rimesso una\r\n * volta con la doppia selezione di @foblex/flow).\r\n *\r\n * Non entra nella cronologia: aprire una finestra non e' una modifica del documento, e un undo\r\n * che aprisse o chiudesse finestre sarebbe un comando che fa due cose.\r\n */\r\n private readonly _isNodeEditorOpen = signal(false);\r\n private readonly _past = signal<readonly string[]>([]);\r\n private readonly _future = signal<readonly string[]>([]);\r\n /**\r\n * Istantanea di riferimento: l'ultimo stato **salvato**. Non e' un contatore di operazioni.\r\n * Contare le mutazioni sembra equivalente e non lo e': dopo un `undo` il contatore sale, cioe'\r\n * dichiara piu' modifiche di prima proprio quando l'utente ne ha disfatta una. Serve a decidere\r\n * se salvare, quindi deve rispondere alla domanda giusta — \"differisce da cio' che e' salvato?\"\r\n * — e tornare a \"no\" quando si annulla fino allo stato di partenza.\r\n */\r\n private readonly _savedSnapshot = signal<string>(\r\n JSON.stringify(dpeEmptyDefinition('', '', 'Sqlite')),\r\n );\r\n /**\r\n * Vero da quando un documento e' stato **caricato**. Distingue il segnaposto del costruttore da\r\n * una definizione vera, e serve a chi non deve lavorare sul segnaposto: la validazione non lo\r\n * manda al backend (`DpeValidationService`), perche' la risposta sarebbero i quattro rilievi di\r\n * una definizione vuota — `name`, `label`, `dataSources` e nessun writeback — che descrivono il\r\n * segnaposto e non il lavoro dell'utente, e che dall'editor nessuno puo' correggere.\r\n */\r\n private readonly _hasDocument = signal(false);\r\n\r\n /** La definizione corrente: unica fonte di verita' per canvas, ispettori e pannelli. */\r\n readonly definition = this._definition.asReadonly();\r\n\r\n /** Vero da quando `load` ha portato un documento: prima c'e' solo il segnaposto. */\r\n readonly hasDocument = this._hasDocument.asReadonly();\r\n\r\n /** Nodi e archi derivati dalla definizione. Ricalcolati solo quando la definizione cambia. */\r\n readonly graph = computed<DpeGraph>(() => dpeBuildGraph(this._definition()));\r\n\r\n readonly selectedNodeNames = this._selectedNodeNames.asReadonly();\r\n\r\n /** Insieme dei selezionati: il canvas lo interroga una volta per nodo a ogni render. */\r\n readonly selectedNodeNameSet = computed<ReadonlySet<string>>(\r\n () => new Set(this._selectedNodeNames()),\r\n );\r\n\r\n /**\r\n * Il nodo che l'ispettore mostra: il **primo** della selezione. Un ispettore per piu' nodi\r\n * insieme mostrerebbe campi che appartengono a nodi diversi, che e' peggio di non mostrarne.\r\n */\r\n readonly selectedNodeName = computed<string | undefined>(() => this._selectedNodeNames().at(0));\r\n\r\n readonly selectedNode = computed<DpeAnyNode | undefined>(() => {\r\n const name = this.selectedNodeName();\r\n return name === undefined ? undefined : dpeFindNode(this._definition(), name)?.node;\r\n });\r\n\r\n /**\r\n * La dialog di modifica del nodo e' aperta. Non e' una seconda identita': il nodo e'\r\n * `selectedNodeName()`.\r\n */\r\n readonly isNodeEditorOpen = this._isNodeEditorOpen.asReadonly();\r\n\r\n // --- cio' che arriva dal backend ------------------------------------------\r\n\r\n readonly validateResult = this._validateResult.asReadonly();\r\n readonly compileResult = this._compileResult.asReadonly();\r\n\r\n readonly diagnostics = computed<readonly Diagnostic[]>(\r\n () => this._validateResult()?.diagnostics ?? [],\r\n );\r\n\r\n readonly schemas = computed<readonly DpeNodeSchemaInfo[]>(\r\n () => this._validateResult()?.schemas ?? [],\r\n );\r\n\r\n readonly executionOrder = computed<readonly string[]>(\r\n () => this._validateResult()?.executionOrder ?? [],\r\n );\r\n\r\n readonly writebackSequence = computed<readonly DpeWritebackStep[]>(\r\n () => this._validateResult()?.writebackSequence ?? [],\r\n );\r\n\r\n /** Schema per nome di nodo: la vista dello schema lo cerca per nodo, non per posizione. */\r\n readonly schemaByNode = computed<ReadonlyMap<string, DpeNodeSchemaInfo>>(() => {\r\n const map = new Map<string, DpeNodeSchemaInfo>();\r\n for (const schema of this.schemas()) {\r\n map.set(schema.nodeName, schema);\r\n }\r\n return map;\r\n });\r\n\r\n /**\r\n * Diagnostiche raggruppate per nodo, piu' quelle che **non** appartengono a nessun nodo\r\n * (radice, parametri, grafo intero): tenerle separate e' l'unico modo perche' un\r\n * `DPE_GRAPH_NO_WRITEBACK` non scompaia solo perche' non ha un nodo a cui attaccarsi.\r\n */\r\n readonly diagnosticsByNode = computed<ReadonlyMap<string, DpeNodeDiagnostics>>(() =>\r\n dpeGroupDiagnosticsByNode(this.diagnostics(), this._definition()),\r\n );\r\n\r\n readonly definitionDiagnostics = computed<readonly Diagnostic[]>(() =>\r\n dpeDefinitionLevelDiagnostics(this.diagnostics(), this._definition()),\r\n );\r\n\r\n /** Posizioni lette da `dpe:layout` (ADR 0013): assenti dove il documento non le porta. */\r\n readonly positions = computed<ReadonlyMap<string, DpeNodePosition>>(() => {\r\n const map = new Map<string, DpeNodePosition>();\r\n for (const located of dpeAllNodes(this._definition())) {\r\n const position = dpeReadNodePosition(located.node);\r\n if (position) {\r\n map.set(located.node.name, position);\r\n }\r\n }\r\n return map;\r\n });\r\n\r\n readonly canUndo = computed(() => this._past().length > 0);\r\n readonly canRedo = computed(() => this._future().length > 0);\r\n\r\n /**\r\n * Vero quando il documento differisce dall'ultimo salvataggio. Il confronto e' fra istantanee\r\n * JSON — la stessa moneta dell'undo/redo — e **non** un conteggio di differenze: un elenco di\r\n * differenze costa quanto un diff strutturale, e nessuno lo legge come numero.\r\n *\r\n * Confronto testuale, quindi sensibile all'ordine delle chiavi. Le mutazioni producono nuovi\r\n * valori per spread, che l'ordine lo conserva, e l'undo riparte dalla stessa serializzazione:\r\n * l'unico esito possibile di una divergenza d'ordine sarebbe un falso \"modificata\", cioe' un\r\n * salvataggio in piu' e mai una perdita.\r\n */\r\n readonly isDirty = computed(() => JSON.stringify(this._definition()) !== this._savedSnapshot());\r\n\r\n // -------------------------------------------------------------------------\r\n // Caricamento e risultati del backend\r\n // -------------------------------------------------------------------------\r\n\r\n /**\r\n * Sostituisce la definizione. Azzera la cronologia: le istantanee della definizione\r\n * precedente non sono passi di quella nuova, e un undo che riportasse a un altro documento\r\n * sarebbe una perdita di dati che l'utente non ha chiesto.\r\n */\r\n load(definition: DpeDefinition): void {\r\n this._definition.set(definition);\r\n this._hasDocument.set(true);\r\n // Cio' che e' appena arrivato **e'** lo stato salvato: aprire un documento non lo sporca.\r\n this._savedSnapshot.set(JSON.stringify(definition));\r\n this._past.set([]);\r\n this._future.set([]);\r\n this._selectedNodeNames.set([]);\r\n // Una dialog rimasta aperta guarderebbe un nodo di **un'altra** definizione: il nome potrebbe\r\n // anche esistere qui, e allora si starebbe modificando un nodo che nessuno ha aperto. E' il\r\n // caso che si dimentica, perche' non fallisce — mostra la cosa sbagliata.\r\n this._isNodeEditorOpen.set(false);\r\n this._validateResult.set(undefined);\r\n this._compileResult.set(undefined);\r\n }\r\n\r\n /** Deposita il risultato della validazione. Nessuna interpretazione: si mostra cio' che c'e'. */\r\n applyValidateResult(result: DpeValidateResult | undefined): void {\r\n this._validateResult.set(result);\r\n }\r\n\r\n applyCompileResult(result: DpeCompileResult | undefined): void {\r\n this._compileResult.set(result);\r\n }\r\n\r\n /**\r\n * L'ospite ha salvato: da qui in poi il riferimento e' il documento **effettivamente\r\n * persistito**, che si passa come argomento.\r\n *\r\n * Il parametro non e' un vezzo: un salvataggio e' asincrono, e nel frattempo l'utente puo'\r\n * avere continuato a scrivere. Fissando il riferimento sullo stato *corrente* si dichiarerebbe\r\n * salvato anche cio' che e' stato scritto dopo l'invio, cioe' esattamente il lavoro che si\r\n * perderebbe. Omesso, vale lo stato corrente: e' il caso di chi salva in modo sincrono.\r\n */\r\n markSaved(saved?: DpeDefinition): void {\r\n this._savedSnapshot.set(JSON.stringify(saved ?? this._definition()));\r\n }\r\n\r\n /**\r\n * **L'unico** punto che decide quale nodo e' selezionato. La selezione ha una sola fonte di\r\n * verita', come la definizione: la classe nel DOM e lo stato di selezione della libreria di\r\n * canvas ne **derivano**, non la stabiliscono. Due scritture indipendenti sullo stesso fatto\r\n * producono due nodi selezionati insieme e un utente che non sa su cosa sta agendo.\r\n */\r\n select(nodeName: string | undefined): void {\r\n this._selectedNodeNames.set(nodeName === undefined ? [] : [nodeName]);\r\n }\r\n\r\n /** Selezione multipla (riquadro, `Ctrl+A`): la lista che arriva dal gesto, senza duplicati. */\r\n selectMany(nodeNames: readonly string[]): void {\r\n this._selectedNodeNames.set(Array.from(new Set(nodeNames)));\r\n }\r\n\r\n /**\r\n * Seleziona **solo** quel nodo e apre la dialog di modifica.\r\n *\r\n * La selezione **collassa a un nodo solo**: il gesto «apri questo» dichiara su chi si sta\r\n * agendo, e la regola gia' in vigore e' che con piu' di un nodo selezionato non si mostrano i\r\n * campi di uno.\r\n *\r\n * Le due scritture sono **esplicite** e non conseguenza di un `effect`: un effect che scrive\r\n * signal renderebbe l'ordine di questi due fatti dipendente dalla schedulazione, cioe' non\r\n * testabile.\r\n */\r\n openNodeEditor(nodeName: string): void {\r\n this.select(nodeName);\r\n this._isNodeEditorOpen.set(true);\r\n }\r\n\r\n closeNodeEditor(): void {\r\n this._isNodeEditorOpen.set(false);\r\n }\r\n\r\n // -------------------------------------------------------------------------\r\n // Mutazioni — sincrone e granulari\r\n // -------------------------------------------------------------------------\r\n\r\n /**\r\n * Proprieta' della radice: etichetta, descrizione, dialetto, parametri.\r\n *\r\n * **`name` e `formatVersion` non sono nell'insieme**, e il tipo lo impedisce invece di dirlo in un\r\n * commento. `name` e' la chiave del catalogo: rinominare e' creare-e-cancellare (ADR 0019), non\r\n * una modifica del documento — e `Partial<DpeDefinition>` permetteva `{ name: undefined }`, che\r\n * lasciava nel documento una chiave senza valore. Da li' il salvataggio partiva verso\r\n * `PUT /definitions/undefined`. `formatVersion` e' del formato, non dell'autore.\r\n */\r\n patchDefinition(patch: Partial<Omit<DpeDefinition, 'name' | 'formatVersion'>>): void {\r\n this.commit({ ...this._definition(), ...patch });\r\n }\r\n\r\n addNode<K extends DpeNodeCollectionName>(collection: K, node: DpeNodeCollectionMap[K]): void {\r\n const definition = this._definition();\r\n const existing = (definition[collection] ?? []) as DpeNodeCollectionMap[K][];\r\n this.commit(this.withCollection(definition, collection, [...existing, node]));\r\n }\r\n\r\n updateNode<K extends DpeNodeCollectionName>(\r\n collection: K,\r\n nodeName: string,\r\n patch: Partial<DpeNodeCollectionMap[K]>,\r\n ): void {\r\n const definition = this._definition();\r\n const existing = (definition[collection] ?? []) as DpeNodeCollectionMap[K][];\r\n let changed = false;\r\n const next = existing.map((node) => {\r\n if (node.name !== nodeName) {\r\n return node;\r\n }\r\n changed = true;\r\n // `name` non si cambia da qui: rinominare e' un refactoring globale e passa da\r\n // `renameNode`. Lasciarlo passare in un patch produrrebbe un documento con tutti i\r\n // riferimenti a valle rotti e nessun segnale.\r\n const { name: _ignored, ...rest } = patch as Partial<DpeNodeCollectionMap[K]> & { name?: string };\r\n void _ignored;\r\n return { ...node, ...rest };\r\n });\r\n if (!changed) {\r\n return;\r\n }\r\n this.commit(this.withCollection(definition, collection, next));\r\n }\r\n\r\n /**\r\n * Rimuove un nodo. I riferimenti a valle **restano** e diventano riferimenti rotti: e' cio'\r\n * che il documento dice davvero, e il backend li segnala (`DPE_REF_UNKNOWN_NODE`). Pulirli\r\n * qui vorrebbe dire decidere al posto dell'utente che quel filtro non serve piu'.\r\n */\r\n removeNode(nodeName: string): void {\r\n const definition = this._definition();\r\n const located = dpeFindNode(definition, nodeName);\r\n if (!located) {\r\n return;\r\n }\r\n // Si legge **prima** della potatura della selezione: dopo, `selectedNodeName()` e' gia' un\r\n // altro nodo (o nessuno) e non si saprebbe piu' se la dialog stava guardando questo.\r\n const wasOpenOnIt = this._isNodeEditorOpen() && this.selectedNodeName() === nodeName;\r\n const existing = (definition[located.collection] ?? []) as DpeAnyNode[];\r\n const next = existing.filter((node) => node.name !== nodeName);\r\n this.commit(this.withCollection(definition, located.collection, next));\r\n // Un nome che non esiste piu' non puo' restare selezionato: sarebbe una selezione che punta\r\n // al vuoto. Si riscrive solo se serve, perche' un nuovo array uguale al precedente farebbe\r\n // ripartire per nulla chi osserva la selezione.\r\n const selected = this._selectedNodeNames();\r\n if (selected.includes(nodeName)) {\r\n this._selectedNodeNames.set(selected.filter((name) => name !== nodeName));\r\n }\r\n // Il bersaglio della dialog e' svanito: dopo l'eliminazione non c'e' piu' niente da\r\n // modificare. La chiusura sta qui e non nel componente perche' «Elimina il nodo» sta nel\r\n // piede della dialog, e un componente che si chiude da se' dopo aver chiesto una mutazione\r\n // sarebbe una seconda regola sullo stesso fatto.\r\n if (wasOpenOnIt) {\r\n this._isNodeEditorOpen.set(false);\r\n }\r\n }\r\n\r\n /**\r\n * L'**unico** punto che rinomina un nodo (**I5**): delega alla funzione pura, che aggiorna\r\n * ogni riferimento a valle passando dall'unico elenco di slot.\r\n */\r\n renameNode(oldName: string, newName: string): void {\r\n if (oldName === newName || newName.length === 0) {\r\n return;\r\n }\r\n this.commit(dpeRenameNode(this._definition(), oldName, newName));\r\n // La selezione segue la rinomina: identifica un nodo per nome, e il nome e' cambiato.\r\n const selected = this._selectedNodeNames();\r\n if (selected.includes(oldName)) {\r\n this._selectedNodeNames.set(selected.map((name) => (name === oldName ? newName : name)));\r\n }\r\n }\r\n\r\n /** Quante occorrenze toccherebbe una rinomina: si dice prima, non dopo. */\r\n countReferences(nodeName: string): number {\r\n return dpeCountReferences(this._definition(), nodeName);\r\n }\r\n\r\n /**\r\n * Crea un nodo della categoria data nella posizione indicata e restituisce il nome\r\n * assegnato, cosi' chi lo ha creato puo' selezionarlo subito.\r\n */\r\n createNodeAt<K extends DpeNodeCollectionName>(collection: K, position: DpeNodePosition): string {\r\n const definition = this._definition();\r\n const node = dpeWriteNodePosition(dpeCreateNode(definition, collection), position);\r\n const existing = (definition[collection] ?? []) as DpeNodeCollectionMap[K][];\r\n this.commit(this.withCollection(definition, collection, [...existing, node]));\r\n return node.name;\r\n }\r\n\r\n /**\r\n * Sposta un nodo scrivendo `dpe:layout`. `silent` non aggiunge un passo di cronologia:\r\n * trascinare cinque nodi insieme e' **un** gesto, e cinque passi di undo per un solo\r\n * trascinamento sono cinque passi che l'utente non ha fatto.\r\n */\r\n moveNode(nodeName: string, position: DpeNodePosition, options?: { silent?: boolean }): void {\r\n const definition = this._definition();\r\n const located = dpeFindNode(definition, nodeName);\r\n if (!located) {\r\n return;\r\n }\r\n const existing = (definition[located.collection] ?? []) as DpeAnyNode[];\r\n const next = existing.map((node) =>\r\n node.name === nodeName ? dpeWriteNodePosition(node, position) : node,\r\n );\r\n const updated = this.withCollection(definition, located.collection, next);\r\n if (options?.silent) {\r\n this._definition.set(updated);\r\n return;\r\n }\r\n this.commit(updated);\r\n }\r\n\r\n /**\r\n * Applica un layout calcolato **su comando dell'utente** («Riordina»): un passo di cronologia\r\n * per l'intero riordino, e il documento risulta modificato, perche' lo e'.\r\n */\r\n applyPositions(positions: ReadonlyMap<string, DpeNodePosition>): void {\r\n this.commit(this.withPositions(this._definition(), positions));\r\n }\r\n\r\n /**\r\n * Layout **iniziale** di un documento che non porta coordinate (ADR 0013: sono opzionali).\r\n *\r\n * Non e' una modifica dell'utente: non entra nella cronologia e **ri-fissa l'istantanea di\r\n * riferimento**, cosi' aprire un documento non produce «modificata». Un indicatore che dice\r\n * «modificata» appena si apre insegna a ignorarlo, e un indicatore ignorato e' peggio di\r\n * nessun indicatore. Il prezzo accettato: chi chiude senza salvare perde le coordinate\r\n * calcolate, che dagre rigenera in modo deterministico.\r\n *\r\n * Il riferimento si sposta **solo** se il documento non era gia' modificato: se l'utente ha\r\n * scritto qualcosa, un layout non puo' dichiarare salvato il suo lavoro.\r\n */\r\n applyInitialPositions(positions: ReadonlyMap<string, DpeNodePosition>): void {\r\n const wasClean = !this.isDirty();\r\n const next = this.withPositions(this._definition(), positions);\r\n this._definition.set(next);\r\n if (wasClean) {\r\n this._savedSnapshot.set(JSON.stringify(next));\r\n }\r\n }\r\n\r\n /**\r\n * Collega una porta di ingresso di `targetNodeName` al nodo `sourceName`. Non verifica che\r\n * il collegamento sia lecito: cicli (**I2**) e riferimenti a valle (**I3**) li rifiuta il\r\n * backend, e l'errore compare subito perche' la validazione parte dopo la mutazione.\r\n */\r\n connect(\r\n targetNodeName: string,\r\n slot: string,\r\n sourceName: string,\r\n index?: number,\r\n ): void {\r\n this.writeReference(targetNodeName, slot, index, sourceName);\r\n }\r\n\r\n /** Scollega una porta: lo slot torna vuoto, non a stringa vuota. */\r\n disconnect(targetNodeName: string, slot: string, index?: number): void {\r\n this.writeReference(targetNodeName, slot, index, undefined);\r\n }\r\n\r\n // -------------------------------------------------------------------------\r\n // Cronologia\r\n // -------------------------------------------------------------------------\r\n\r\n undo(): void {\r\n const past = this._past();\r\n if (past.length === 0) {\r\n return;\r\n }\r\n const snapshot = past[past.length - 1];\r\n this._past.set(past.slice(0, -1));\r\n this._future.set([...this._future(), JSON.stringify(this._definition())]);\r\n this._definition.set(JSON.parse(snapshot) as DpeDefinition);\r\n }\r\n\r\n redo(): void {\r\n const future = this._future();\r\n if (future.length === 0) {\r\n return;\r\n }\r\n const snapshot = future[future.length - 1];\r\n this._future.set(future.slice(0, -1));\r\n this._past.set([...this._past(), JSON.stringify(this._definition())]);\r\n this._definition.set(JSON.parse(snapshot) as DpeDefinition);\r\n }\r\n\r\n // -------------------------------------------------------------------------\r\n // Interno\r\n // -------------------------------------------------------------------------\r\n\r\n private writeReference(\r\n targetNodeName: string,\r\n slot: string,\r\n index: number | undefined,\r\n sourceName: string | undefined,\r\n ): void {\r\n const definition = this._definition();\r\n const located = dpeFindNode(definition, targetNodeName);\r\n if (!located) {\r\n return;\r\n }\r\n const existing = (definition[located.collection] ?? []) as DpeAnyNode[];\r\n const next = existing.map((node) =>\r\n node.name === targetNodeName\r\n ? dpeWriteSourceReference(located.collection, node, slot, index, sourceName)\r\n : node,\r\n );\r\n this.commit(this.withCollection(definition, located.collection, next));\r\n }\r\n\r\n /** Scrive le posizioni su tutte le collezioni producendo una nuova definizione. */\r\n private withPositions(\r\n definition: DpeDefinition,\r\n positions: ReadonlyMap<string, DpeNodePosition>,\r\n ): DpeDefinition {\r\n let next = definition;\r\n for (const collection of DPE_NODE_COLLECTIONS) {\r\n const nodes = next[collection] as DpeAnyNode[] | undefined;\r\n if (!nodes || nodes.length === 0) {\r\n continue;\r\n }\r\n const updated = nodes.map((node) => {\r\n const position = positions.get(node.name);\r\n return position ? dpeWriteNodePosition(node, position) : node;\r\n });\r\n next = this.withCollection(next, collection, updated);\r\n }\r\n return next;\r\n }\r\n\r\n /**\r\n * Sostituisce una collezione producendo una nuova definizione. Il cast e' confinato qui:\r\n * TypeScript non correla la chiave di collezione con il tipo di nodo dentro un accesso\r\n * dinamico, e la mappa dei tipi serve ai chiamanti, non a questa riga.\r\n */\r\n private withCollection(\r\n definition: DpeDefinition,\r\n collection: DpeNodeCollectionName,\r\n nodes: readonly unknown[],\r\n ): DpeDefinition {\r\n return { ...definition, [collection]: nodes.slice() } as DpeDefinition;\r\n }\r\n\r\n private commit(next: DpeDefinition): void {\r\n const snapshot = JSON.stringify(this._definition());\r\n const past = [...this._past(), snapshot];\r\n this._past.set(past.length > HISTORY_LIMIT ? past.slice(past.length - HISTORY_LIMIT) : past);\r\n // Una nuova mutazione chiude il ramo di redo: tenerlo sarebbe una cronologia ad albero,\r\n // che nessun comando dell'editor sa percorrere.\r\n this._future.set([]);\r\n this._definition.set(next);\r\n }\r\n}\r\n","/**\r\n * Lo stato del **catalogo** delle definizioni (ADR 0019): elenco, esiti delle chiamate, comandi.\r\n *\r\n * `@Injectable()` **senza `providedIn`**, come gli altri store: e' un provider del componente\r\n * radice della schermata di catalogo, non della root dell'applicazione. Due cataloghi in due\r\n * punti dell'ospite sono due elenchi indipendenti.\r\n *\r\n * Sta in `core/` e **non conosce il DOM**: e' il posto in cui un difetto e' silenzioso — una\r\n * chiamata mancata, un `refresh` che non parte dopo una scrittura, un errore assorbito — e quindi\r\n * il posto che ha bisogno di essere testabile senza browser.\r\n *\r\n * Cosa questo store **non** fa, e non e' una dimenticanza:\r\n *\r\n * - **non riordina** i sommari: l'ordine e' quello che l'ospite ha restituito (`contracts.md` §7),\r\n * e riordinarlo lato client combatterebbe una scelta che l'ospite ha gia' fatto;\r\n * - **non filtra**: il filtro di testo e' locale al componente, sui sommari gia' ricevuti;\r\n * - **non calcola** `nodeCount` ne' `hasErrors`: sono fatti riportati dall'ospite, e dedurli qui\r\n * sarebbe una validazione fatta dal client per interposta persona;\r\n * - **non carica documenti**: aprire e' dell'ospite, che potrebbe instradare o controllare\r\n * permessi. Il catalogo emette un nome.\r\n */\r\n\r\nimport { DestroyRef, Injectable, computed, inject, signal } from '@angular/core';\r\nimport { takeUntilDestroyed } from '@angular/core/rxjs-interop';\r\nimport { EMPTY, Observable, catchError, map, switchMap, tap } from 'rxjs';\r\n\r\nimport { DpeBuilderApi } from '../api/dpe-builder-api';\r\nimport { DpeApiError } from '../api/dpe-api-error';\r\nimport { DpeDefinitionSummary } from '../model/dpe-api.model';\r\nimport { SqlDialectKind } from '../model/dpe-definition.model';\r\nimport { dpeCreateCommandAvailability, dpeReadCapabilities } from './dpe-capabilities';\r\nimport { dpeEmptyDefinition } from './dpe-document.store';\r\n\r\n@Injectable()\r\nexport class DpeCatalogStore {\r\n private readonly api = inject(DpeBuilderApi);\r\n private readonly destroyRef = inject(DestroyRef);\r\n\r\n /**\r\n * Le primitive disponibili, fotografate **alla costruzione** per identita' del metodo\r\n * (`dpe-capabilities.ts`). La disponibilita' si sa prima del clic: un comando che sembra attivo\r\n * e fallisce alla pressione insegna a non fidarsi dei bottoni.\r\n */\r\n readonly capabilities = dpeReadCapabilities(this.api);\r\n\r\n /** Il comando di creazione dipende da **due** primitive, e il motivo va mostrato. */\r\n readonly createCommand = dpeCreateCommandAvailability(this.capabilities);\r\n\r\n private readonly _summaries = signal<readonly DpeDefinitionSummary[]>([]);\r\n private readonly _isLoading = signal(false);\r\n private readonly _isMutating = signal(false);\r\n private readonly _error = signal<DpeApiError | null>(null);\r\n private readonly _hasLoaded = signal(false);\r\n private readonly _environmentDialect = signal<SqlDialectKind | undefined>(undefined);\r\n\r\n /** Nell'ordine restituito dall'ospite, mai riordinato. */\r\n readonly summaries = this._summaries.asReadonly();\r\n\r\n /**\r\n * Due stati distinti e non uno: **elencare** e **scrivere** si vedono diversi. Un unico\r\n * \"occupato\" farebbe lampeggiare l'elenco a ogni cancellazione, e nasconderebbe il fatto che\r\n * dopo una scrittura l'elenco si ricarica.\r\n */\r\n readonly isLoading = this._isLoading.asReadonly();\r\n readonly isMutating = this._isMutating.asReadonly();\r\n\r\n readonly error = this._error.asReadonly();\r\n\r\n /** Vero dopo la prima risposta: prima, un elenco vuoto non significa «nessuna definizione». */\r\n readonly hasLoaded = this._hasLoaded.asReadonly();\r\n\r\n /**\r\n * Il dialetto **dichiarato dall'ambiente** (ADR 0017), che e' quello con cui nasce una\r\n * definizione creata da qui. Non si chiede all'utente e non si sceglie per ripiego: senza\r\n * dichiarazione il comando di creazione resta spento (`createCommand`).\r\n */\r\n readonly environmentDialect = this._environmentDialect.asReadonly();\r\n\r\n readonly isEmpty = computed(() => this._hasLoaded() && this._summaries().length === 0);\r\n\r\n constructor() {\r\n // L'elenco si chiede all'apertura: un catalogo che mostra righe solo dopo un clic su\r\n // «Aggiorna» e' un catalogo che sembra vuoto.\r\n this.refresh();\r\n\r\n // La dichiarazione dell'ambiente e' un fatto dell'installazione: si chiede una volta. Un\r\n // guasto qui non e' un errore del catalogo — resta il comportamento di `getEnvironment`\r\n // assente, cioe' creazione spenta.\r\n if (this.capabilities.getEnvironment) {\r\n this.api\r\n .getEnvironment()\r\n .pipe(\r\n tap((environment) => this._environmentDialect.set(environment.dialect)),\r\n catchError(() => EMPTY),\r\n takeUntilDestroyed(this.destroyRef),\r\n )\r\n .subscribe();\r\n }\r\n }\r\n\r\n /**\r\n * Ricarica l'elenco. Senza `listDefinitions` non e' un errore: e' un ambiente che non offre il\r\n * catalogo, e chi rende lo dice guardando `capabilities.listDefinitions`.\r\n */\r\n refresh(): void {\r\n if (!this.capabilities.listDefinitions) {\r\n return;\r\n }\r\n this._isLoading.set(true);\r\n this._error.set(null);\r\n this.api\r\n .listDefinitions()\r\n .pipe(\r\n tap((summaries) => {\r\n this._summaries.set(summaries);\r\n this._isLoading.set(false);\r\n this._hasLoaded.set(true);\r\n }),\r\n catchError((error: unknown) => this.fail(error, () => this._isLoading.set(false))),\r\n takeUntilDestroyed(this.destroyRef),\r\n )\r\n .subscribe();\r\n }\r\n\r\n /**\r\n * Crea una definizione **subito**, non al primo salvataggio: `save` significa «scrivi questa\r\n * definizione» e non distingue inserimento da aggiornamento, quindi solo `createDefinition` da'\r\n * all'ospite il punto in cui rifiutare un nome duplicato. Una definizione che esiste solo nel\r\n * browser e' inoltre uno stato che l'elenco non puo' mostrare.\r\n *\r\n * Il dialetto arriva dall'ambiente e non dall'utente (ADR 0017): lo passa il chiamante, che lo\r\n * legge da `environmentDialect`.\r\n *\r\n * L'osservabile e' **freddo**: chi comanda si iscrive **una volta** e riceve il sommario che\r\n * l'ospite ha restituito — non quello costruito qui, perche' il nome puo' essere stato\r\n * normalizzato.\r\n */\r\n create(name: string, label: string, dialect: SqlDialectKind): Observable<DpeDefinitionSummary> {\r\n return this.mutate(this.api.createDefinition(dpeEmptyDefinition(name, label, dialect)));\r\n }\r\n\r\n /**\r\n * Duplica: `load` **e poi** `createDefinition`. Non esiste una primitiva di duplicazione sul\r\n * contratto, e non deve esistere — si comporrebbe di queste due comunque, e una primitiva\r\n * dedicata dovrebbe ridecidere lato host cosa cambia.\r\n *\r\n * Il dialetto e' quello della **definizione di partenza**, non quello dell'ambiente: cambiarlo\r\n * duplicando sarebbe una migrazione silenziosa: il dialetto decide le capacita' con cui si\r\n * compila, e se il compilatore di formule non lo copre la copia non compila nessuna espressione\r\n * (ADR 0031 §5).\r\n */\r\n duplicate(\r\n sourceName: string,\r\n name: string,\r\n label: string,\r\n ): Observable<DpeDefinitionSummary> {\r\n return this.mutate(\r\n this.api\r\n .load(sourceName)\r\n .pipe(switchMap((source) => this.api.createDefinition({ ...source, name, label }))),\r\n );\r\n }\r\n\r\n /** Cancella per nome e ricarica l'elenco. La conferma e' della schermata, non dello store. */\r\n remove(name: string): void {\r\n if (!this.capabilities.deleteDefinition) {\r\n return;\r\n }\r\n this._isMutating.set(true);\r\n this._error.set(null);\r\n this.api\r\n .deleteDefinition(name)\r\n .pipe(\r\n tap(() => {\r\n this._isMutating.set(false);\r\n this.refresh();\r\n }),\r\n catchError((error: unknown) => this.fail(error, () => this._isMutating.set(false))),\r\n takeUntilDestroyed(this.destroyRef),\r\n )\r\n .subscribe();\r\n }\r\n\r\n dismissError(): void {\r\n this._error.set(null);\r\n }\r\n\r\n /**\r\n * Il tratto comune delle due scritture: stato di attesa, **`refresh` dopo il successo** — un\r\n * elenco che omette cio' che l'utente ha appena creato e' un elenco che mente — ed errore nel\r\n * signal invece che propagato. Chi comanda non deve trattare l'errore due volte.\r\n */\r\n private mutate(source: Observable<DpeDefinitionSummary>): Observable<DpeDefinitionSummary> {\r\n this._error.set(null);\r\n return source.pipe(\r\n /*\r\n * La risposta di una scrittura **e' un sommario**, e chi crea apre il nome che c'e' dentro\r\n * (puo' essere stato normalizzato). Se il nome non c'e', l'apertura partirebbe con\r\n * `undefined` e il difetto ricomparirebbe due schermate dopo: qui diventa subito un errore\r\n * nel banner del catalogo, che nomina la causa.\r\n */\r\n map((summary) => {\r\n if (typeof summary?.name !== 'string' || summary.name.trim().length === 0) {\r\n throw new DpeApiError(\r\n 'Backend',\r\n \"La scrittura non ha restituito un sommario con `name`: senza il nome non c'e' niente \" +\r\n 'da aprire. Un backend che risponde `204`, che restituisce il documento invece del ' +\r\n 'sommario, o che risponde in PascalCase produce esattamente questo.',\r\n summary,\r\n );\r\n }\r\n return summary;\r\n }),\r\n tap({\r\n subscribe: () => this._isMutating.set(true),\r\n next: () => {\r\n this._isMutating.set(false);\r\n this.refresh();\r\n },\r\n }),\r\n catchError((error: unknown) => this.fail(error, () => this._isMutating.set(false))),\r\n );\r\n }\r\n\r\n /** Un errore del provider finisce in `error()` e **non lancia**: nessuno lo raccoglierebbe. */\r\n private fail(error: unknown, reset: () => void): Observable<never> {\r\n reset();\r\n this._error.set(this.toApiError(error));\r\n return EMPTY;\r\n }\r\n\r\n /** Cio' che l'host non categorizza diventa `Transport`, la categoria \"riprovabile\". */\r\n private toApiError(error: unknown): DpeApiError {\r\n if (DpeApiError.is(error)) {\r\n return error;\r\n }\r\n const message = error instanceof Error ? error.message : 'Errore inatteso dal backend.';\r\n return new DpeApiError('Transport', message, error);\r\n }\r\n}\r\n","/**\r\n * Le condizioni dei campi condizionali, **in un punto solo**.\r\n *\r\n * Un campo condizionale e' un campo del modello che ha significato solo quando un altro campo del\r\n * **suo stesso nodo** — il discriminante — vale una certa cosa: `jsonOptions` solo con\r\n * `target === 'Json'`, `expressionFields` solo con `kind` diverso da `Slice`, e cosi' via. Le\r\n * condizioni sono usate dall'ispettore in due direzioni opposte:\r\n *\r\n * 1. **resa** (`frontend.md` §5-ter, normativa): il controllo si rende quando la condizione vale\r\n * **oppure** quando il campo porta un valore. Un valore fuori condizione che nessun controllo\r\n * mostra non e' cancellabile dall'editor, e per **I7** sopravvive a ogni salvataggio;\r\n * 2. **ripulitura**: cambiare il discriminante e' un gesto deliberato con cui l'autore dichiara\r\n * inapplicabili i campi che il valore nuovo non governa, e quei campi escono dal documento.\r\n *\r\n * Fino a LS9 le stesse condizioni erano scritte **due volte in ogni ispettore**, una per direzione,\r\n * a pochi metodi di distanza. E' la duplicazione piu' facile da introdurre e la piu' difficile da\r\n * vedere, perche' i sintomi di una divergenza sono silenziosi e nessun test li coglie:\r\n *\r\n * - un campo **ripulito che resta mostrato** — la ripulitura lo toglie, la resa lo rimette, e\r\n * l'utente vede un controllo che non corrisponde a niente nel documento;\r\n * - §5-ter che **smette di coprire un campo** — la resa lo nasconde mentre il documento lo porta,\r\n * cioe' il difetto che quella regola esiste per chiudere, tornato in silenzio.\r\n *\r\n * Con la tabella qui sotto le due direzioni leggono **la stessa riga**: `dpeFieldVisibility()`\r\n * costruisce un segnale per ogni voce, `dpePruneInapplicable()` scorre le voci una per una.\r\n * Aggiungere un campo condizionale domani significa scrivere **una** riga; scriverla per la resa e\r\n * dimenticarla per la ripulitura non e' possibile, perche' e' la stessa riga.\r\n *\r\n * ## Cio' che questo file NON chiude, e va detto\r\n *\r\n * **La fonte autorevole di queste condizioni e' il backend**, e resta ricopiata a mano:\r\n * `dpe/dpe/Validation/ModelRules.cs` e `dpe/dpe/Validation/WritebackRules.cs`. Qui resta una\r\n * **regola di dominio in TypeScript**, cioe' una deroga dichiarata a un vincolo del progetto\r\n * (`frontend.md` §5-ter, «duplicazione dichiarata e temporanea»). `npm run check:modelli` **non**\r\n * la copre: confronta le forme dei tipi, non le condizioni.\r\n *\r\n * Quindi: chi tocca una condizione di la' deve toccarla anche di qua, e i due sintomi silenziosi\r\n * elencati sopra valgono identici per una divergenza dal backend — con l'aggravante che li' non\r\n * c'e' nemmeno il compilatore. La direzione che chiude la classe invece di sorvegliarla (la\r\n * tabella come **dato** pubblicato dal contratto, letta da `ModelRules`) e' la questione aperta 32.\r\n *\r\n * ## Cosa non sta in tabella, e perche'\r\n *\r\n * Solo i campi **del nodo**: la ripulitura toglie chiavi dal nodo, quindi una voce deve essere una\r\n * chiave del nodo. Una condizione che governa un campo **dentro una riga di una collezione** (il\r\n * tipo dichiarato di un campo di sorgente) non e' rappresentabile qui e resta scritta nel suo\r\n * ispettore, dove la meta' «porta un valore» e' un `some(...)` sulle righe.\r\n */\r\n\r\nimport { Signal, computed } from '@angular/core';\r\n\r\nimport {\r\n DataSourceNode,\r\n FilterNode,\r\n HierarchyPathNode,\r\n JoinNode,\r\n TransformNode,\r\n WritebackNode,\r\n} from '../model/dpe-definition.model';\r\n\r\n/** La condizione di applicabilita' di un campo, valutata sul nodo intero. */\r\nexport type DpeFieldCondition<TNode> = (node: TNode) => boolean;\r\n\r\n/**\r\n * La tabella di un tipo di nodo: **chiave del nodo → condizione**.\r\n *\r\n * Le chiavi sono limitate a `keyof TNode` cosi' un campo rinominato nel modello non lascia qui una\r\n * riga orfana che nessuno consuma, e la ripulitura non puo' togliere una chiave inventata.\r\n */\r\nexport type DpeConditionalFields<TNode> = {\r\n readonly [K in keyof TNode]?: DpeFieldCondition<TNode>;\r\n};\r\n\r\n/** Un segnale di visibilita' per ogni voce della tabella, con lo stesso nome del campo. */\r\nexport type DpeFieldVisibility<TConditions> = {\r\n readonly [K in keyof TConditions]: Signal<boolean>;\r\n};\r\n\r\n/**\r\n * «Il campo porta un valore», nel senso di `frontend.md` §5-ter.\r\n *\r\n * Nessuna delle equivalenze qui sotto e' un giudizio sul valore — dire che e' sbagliato tocca al\r\n * backend, e la regola di resa vale **diagnostica o no**:\r\n *\r\n * - **stringa vuota = assente**, perche' il backend confronta con `string.IsNullOrEmpty`: un\r\n * `processingGroupFieldName: ''` arrivato da fuori non deve far comparire un campo vuoto, che\r\n * sarebbe il rumore che la regola promette di non introdurre;\r\n * - **`false` = assente**, perche' i campi governati sono `bool` **non nullable** con default\r\n * `false` lato C# (`OnlyChangedRows`, `AllowsMultipleLookupValues`): dopo la deserializzazione\r\n * `false` e' indistinguibile dall'assenza;\r\n * - **collezione vuota = assente**: un `criteria: []` non dichiara niente, e il backend lo conta\r\n * con `Count(...) == 0`.\r\n *\r\n * Un oggetto invece **e' sempre** una dichiarazione, anche `{}`: per il backend un `jsonOptions`\r\n * non nullo fuori dal proprio target e' `DPE_MODEL_CONDITIONAL_FORBIDDEN` qualunque cosa contenga.\r\n */\r\nexport function dpeDeclares(value: unknown): boolean {\r\n if (value === undefined || value === null || value === '' || value === false) {\r\n return false;\r\n }\r\n return Array.isArray(value) ? value.length > 0 : true;\r\n}\r\n\r\n/**\r\n * I segnali di resa di §5-ter: **la condizione vale oppure il campo porta un valore**.\r\n *\r\n * Si costruiscono dalla tabella intera e non uno per uno, cosi' il segnale di un campo nuovo esiste\r\n * per il solo fatto che la sua riga esiste. Nel template il tipo fa il resto: `shows.jsonOptions()`\r\n * su una voce che non c'e' non compila, quindi non si puo' nascondere un campo per distrazione.\r\n */\r\nexport function dpeFieldVisibility<TNode extends object, TConditions extends DpeConditionalFields<TNode>>(\r\n node: Signal<TNode>,\r\n conditions: TConditions,\r\n): DpeFieldVisibility<TConditions> {\r\n const visibility: Record<string, Signal<boolean>> = {};\r\n for (const [field, condition] of Object.entries(conditions) as [string, DpeFieldCondition<TNode>][]) {\r\n visibility[field] = computed(() => {\r\n const current = node();\r\n return condition(current) || dpeDeclares((current as Record<string, unknown>)[field]);\r\n });\r\n }\r\n return visibility as DpeFieldVisibility<TConditions>;\r\n}\r\n\r\n/**\r\n * La modifica di un discriminante, **piu'** la rimozione dei campi che con il valore nuovo non si\r\n * applicano piu'.\r\n *\r\n * La condizione si valuta sul nodo **come sara' dopo il gesto** (`{ ...node, ...change }`), non su\r\n * quello di adesso: e' il solo modo di trattare un discriminante che ne cambia un altro nello\r\n * stesso gesto — su un writeback la destinazione decide l'operazione, e l'operazione decide il\r\n * campo di identificazione esterna.\r\n *\r\n * E si ripete **finche' non si stabilizza**, perche' una rimozione puo' invalidare la condizione\r\n * di un altro campo: portando una scrittura `Upsert` da `Database` a `Json`, il primo giro toglie\r\n * `operation` e solo il secondo si accorge che `externalIdFieldName` non e' piu' governato da\r\n * niente. Con un giro solo quel valore restava nel documento su un artefatto, dove il backend\r\n * **non lo diagnostica**: ignorato e invisibile, il caso peggiore. Trovato **eseguendo** la demo,\r\n * non leggendo il codice — la versione a un giro passava tutti i test che c'erano, e il test che\r\n * la coglie e' stato scritto dopo averla vista fallire a schermo.\r\n *\r\n * Il punto fisso esiste sempre: ogni giro puo' solo **aggiungere** rimozioni, i campi sono finiti\r\n * e un campo tolto non torna. Una condizione scritta al contrario (vera **perche'** l'altro campo\r\n * e' assente) renderebbe il risultato dipendente dall'ordine: in tabella non ce ne sono, e questo\r\n * e' il motivo per cui non ce ne devono essere.\r\n *\r\n * **Si toglie cio' che non si applica, non cio' che apparteneva al valore precedente**, e la\r\n * differenza non e' teorica: `Expression` → `ComputeRelative` non tocca `expressionFields`, perche'\r\n * quel campo e' governato da entrambi. Un «cambiando tipo si azzera» sarebbe piu' semplice da\r\n * scrivere e cancellerebbe espressioni SQL su un passaggio che non le rende inapplicabili.\r\n *\r\n * Togliere e' **rimuovere la chiave** (`undefined`), non scriverci `false` o `[]`: le due forme\r\n * sono equivalenti per il validatore e diverse per il documento, e il documento e' cio' che si\r\n * salva (**I7**).\r\n *\r\n * Si rimuove **solo cio' che c'e'**: un campo gia' assente non entra nel patch. Sul documento\r\n * salvato non cambia niente — una chiave a `undefined` sparisce serializzando — ma il patch\r\n * diventa il resoconto esatto del gesto, e «questo cambio non tocca niente» diventa una cosa\r\n * osservabile invece di una cosa da credere.\r\n *\r\n * Non e' distruttivo perche' il patch passa da `DpeDocumentStore.updateNode` → `commit`, che mette\r\n * l'istantanea precedente nella cronologia: «Annulla» rimette tutto. Verificato a schermo in\r\n * LS7-quater, non dedotto — era il solo fatto che avrebbe potuto togliere la ragione a questo\r\n * comportamento.\r\n */\r\nexport function dpePruneInapplicable<TNode extends object, TConditions extends DpeConditionalFields<TNode>>(\r\n node: TNode,\r\n change: Partial<TNode>,\r\n conditions: TConditions,\r\n): Partial<TNode> {\r\n const entries = Object.entries(conditions) as [string, DpeFieldCondition<TNode>][];\r\n const next = { ...node, ...change } as Record<string, unknown>;\r\n const patch: Record<string, unknown> = { ...change };\r\n let removedSomething = true;\r\n while (removedSomething) {\r\n removedSomething = false;\r\n for (const [field, condition] of entries) {\r\n // `!== undefined` e non `dpeDeclares(...)`: un `onlyChangedRows: false` o un `criteria: []`\r\n // sono chiavi **presenti** nel documento, e vanno tolte anche se non dichiarano niente. E'\r\n // anche la guardia che chiude il giro: un campo gia' tolto non si ritoglie.\r\n if (next[field] === undefined || condition(next as TNode)) {\r\n continue;\r\n }\r\n next[field] = undefined;\r\n patch[field] = undefined;\r\n removedSomething = true;\r\n }\r\n }\r\n return patch as Partial<TNode>;\r\n}\r\n\r\n/**\r\n * Sorgente: il tipo decide da dove si legge (`ModelRules.cs`, `ApplyDataSources`).\r\n *\r\n * Nessuno dei tre e' diagnosticato **fuori** condizione — il backend li richiede dove servono e\r\n * ignora il resto — e non cambia niente: §5-ter guarda il documento, non l'elenco dei codici.\r\n */\r\nexport const DPE_DATA_SOURCE_CONDITIONS = {\r\n tableName: (node) => node.kind === 'Table',\r\n csvOptions: (node) => node.kind === 'Csv',\r\n externalDatasetName: (node) => node.kind === 'ExternalDataset',\r\n} satisfies DpeConditionalFields<DataSourceNode>;\r\n\r\n/**\r\n * Filtro: `isDynamic` sceglie quale meta' del nodo vale (`ModelRules.cs`, `ApplyFilters`).\r\n *\r\n * Il parametro serve al filtro dinamico; criteri e condizione a quello statico.\r\n */\r\nexport const DPE_FILTER_CONDITIONS = {\r\n criteria: (node) => !node.isDynamic,\r\n condition: (node) => !node.isDynamic,\r\n filterParameterName: (node) => !!node.isDynamic,\r\n} satisfies DpeConditionalFields<FilterNode>;\r\n\r\n/**\r\n * Giunzione: la molteplicita' ammessa ha significato solo su una `Lookup`\r\n * (`JoinRules.cs`, `DialectRules.cs`).\r\n */\r\nexport const DPE_JOIN_CONDITIONS = {\r\n allowsMultipleLookupValues: (node) => node.kind === 'Lookup',\r\n} satisfies DpeConditionalFields<JoinNode>;\r\n\r\n/** Percorso gerarchico: le aggregazioni servono quando sono dichiarate richieste. */\r\nexport const DPE_HIERARCHY_PATH_CONDITIONS = {\r\n aggregateFields: (node) => !!node.isAggregationRequired,\r\n} satisfies DpeConditionalFields<HierarchyPathNode>;\r\n\r\n/**\r\n * Trasformazione: `kind` governa i quattro campi (`ModelRules.cs`, `ApplyTransforms`).\r\n *\r\n * `expressionFields` e' governato da **due** tipi, ed e' il motivo per cui la ripulitura non puo'\r\n * essere un azzeramento.\r\n */\r\nexport const DPE_TRANSFORM_CONDITIONS = {\r\n expressionFields: (node) => node.kind !== 'Slice',\r\n droppedFields: (node) => node.kind === 'Slice',\r\n partitionBy: (node) => node.kind === 'ComputeRelative',\r\n orderBy: (node) => node.kind === 'ComputeRelative',\r\n} satisfies DpeConditionalFields<TransformNode>;\r\n\r\n/**\r\n * Scrittura: `target` governa sei campi, **`operation` ne governa un settimo**\r\n * (`ModelRules.cs`, `ApplyWritebacks`; `WritebackRules.cs`, `CheckArtifactOptions`,\r\n * `CheckProcessingGroupField`, `CheckFailureHandling`).\r\n *\r\n * `tableName` e `onlyChangedRows` sono in tabella pur **non** essendo diagnosticati fuori da\r\n * `Database`: nessuna regola li vieta, quindi il valore non e' «invalido», e' **ignorato e\r\n * invisibile** — che e' peggio, perche' niente lo segnala e un cambio di destinazione lo puo'\r\n * rendere attivo di colpo. Far dipendere questa tabella dall'elenco dei codici diagnostici\r\n * accoppierebbe l'ispettore a un'altra regola di dominio ancora.\r\n *\r\n * `externalIdFieldName` dipende da `operation`, **non** da `target`: e' la condizione che il\r\n * backend applica (`Operation == Upsert`, richiesto li' e vietato altrove) ed e' la sola scritta\r\n * qui. Fino a LS9 la ripulitura ne usava una seconda, piu' larga (`Target == Database`): era\r\n * proprio la divergenza interna che questo file esiste per rendere impossibile.\r\n *\r\n * `failureHandling` e' vietato dal backend anche dentro un gruppo atomico, e qui non si dice: la\r\n * condizione di resa piu' larga fa comparire il controllo — con il rilievo sotto — invece di\r\n * nascondere un valore che l'autore dovrebbe poter togliere. Restringerla farebbe sparire il\r\n * controllo proprio dove serve.\r\n */\r\nexport const DPE_WRITEBACK_CONDITIONS = {\r\n tableName: (node) => node.target === 'Database',\r\n operation: (node) => node.target === 'Database',\r\n externalIdFieldName: (node) => node.operation === 'Upsert',\r\n onlyChangedRows: (node) => node.target === 'Database',\r\n processingGroupFieldName: (node) => node.target === 'Database',\r\n failureHandling: (node) => node.target === 'Database',\r\n jsonOptions: (node) => node.target === 'Json',\r\n csvOptions: (node) => node.target === 'Csv',\r\n} satisfies DpeConditionalFields<WritebackNode>;\r\n","/**\r\n * La lettura dei **tre esiti** del catalogo delle formule — `contracts.md` §7, normativo.\r\n *\r\n * Sta in una funzione pura e non in un `if` dentro un componente per la ragione che ha reso questo\r\n * lotto necessario: la versione precedente del contratto consegnava il catalogo **nudo**, quindi i\r\n * tre esiti del service ne diventavano due, e *dialetto non coperto* e *backend rotto* arrivavano\r\n * a schermo identici. Un degrado silenzioso non si vede rileggendo un componente; si vede\r\n * scrivendo una tabella e provandola.\r\n *\r\n * **Il discriminante e' `diagnostics`, non `catalog`.** E' la riga che vale piu' di tutte le\r\n * altre, perche' la stesura precedente diceva il contrario: con `ProvidesCatalog == false` il\r\n * catalogo arriva **presente e vuoto**, con il nome del linguaggio e zero diagnostiche — quindi\r\n * un editor che ramasse su `catalog == null` non entrerebbe mai in quel caso, e mostrerebbe un\r\n * fallimento dove non c'e' niente da segnalare.\r\n *\r\n * `catalog` nullo o assente significa **una cosa sola: non e' stato prodotto**.\r\n */\r\n\r\nimport {\r\n DpeExpressionForm,\r\n DpeFormulaCatalog,\r\n DpeFormulaCatalogResult,\r\n} from '../model/dpe-api.model';\r\nimport { DPE_CATALOG_UNAVAILABLE, Diagnostic } from '../model/dpe-diagnostics.model';\r\n\r\n/**\r\n * I quattro stati in cui il campo formula puo' trovarsi rispetto al catalogo — tre del contratto\r\n * piu' «non lo so ancora», che e' quello in cui il campo nasce.\r\n *\r\n * Unione discriminata e non un oggetto con quattro campi opzionali: cosi' «fallito **e** con\r\n * tavolozza» non e' rappresentabile, e chi rende non deve decidere quale dei due vince.\r\n */\r\nexport type DpeFormulaCatalogOutcome =\r\n /** Nessuna risposta conservata: non e' stato chiesto, e' in volo, o la richiesta e' fallita. */\r\n | { readonly kind: 'Unknown' }\r\n /** Catalogo consegnato **con contenuto**: si mostra la tavolozza. */\r\n | { readonly kind: 'Palette'; readonly catalog: DpeFormulaCatalog }\r\n /**\r\n * Catalogo consegnato **vuoto** e nessuna diagnostica: il compilatore non descrive il proprio\r\n * linguaggio, che non e' un errore. Niente tavolozza e **nessun rumore**; `languageName` c'e' ed\r\n * e' l'unica cosa da mostrare — e l'etichetta del campo la prende comunque dall'ambiente.\r\n */\r\n | { readonly kind: 'Silent'; readonly languageName?: string }\r\n /**\r\n * Diagnostiche non vuote: il catalogo non e' stato prodotto e **il perche' si mostra**.\r\n *\r\n * `lead` e' la riga in testa, `details` tutto il resto **nell'ordine ricevuto**. La separazione\r\n * non e' estetica: il sacco strutturale riguarda l'intera definizione e puo' contenere voci che\r\n * con il nodo richiesto non c'entrano, filtrarle per pertinenza non e' affidabile (l'errore che\r\n * spiega lo schema puo' stare a piu' salti di distanza), quindi il rimedio dichiarato e' una\r\n * riga in testa e il resto come dettaglio.\r\n */\r\n | {\r\n readonly kind: 'Failed';\r\n readonly lead: Diagnostic;\r\n readonly details: readonly Diagnostic[];\r\n readonly hasError: boolean;\r\n };\r\n\r\n/**\r\n * Legge l'involucro e dice in quale dei quattro stati ci si trova.\r\n *\r\n * L'ordine dei controlli e' il contratto messo in fila, e non e' intercambiabile:\r\n *\r\n * 1. nessun risultato -> `Unknown`;\r\n * 2. **diagnostiche non vuote** -> `Failed`, indipendentemente da `catalog`;\r\n * 3. catalogo con contenuto -> `Palette`;\r\n * 4. tutto il resto -> `Silent`, cioe' niente tavolozza e nessun rumore.\r\n *\r\n * Il quarto ramo assorbe anche il caso fuori contratto «nessun catalogo e nessuna diagnostica»:\r\n * a schermo e' indistinguibile dal terzo esito — niente da mostrare e niente da segnalare — e\r\n * inventare un quinto stato per rimproverare il backend darebbe all'utente un messaggio su cui non\r\n * puo' agire.\r\n */\r\nexport function dpeFormulaCatalogOutcome(\r\n result: DpeFormulaCatalogResult | undefined,\r\n): DpeFormulaCatalogOutcome {\r\n if (!result) {\r\n return { kind: 'Unknown' };\r\n }\r\n const diagnostics = result.diagnostics ?? [];\r\n if (diagnostics.length > 0) {\r\n /*\r\n * `DPE_CATALOG_UNAVAILABLE` va **in testa** quando c'e': nomina la sorgente su cui agire. Se\r\n * non c'e' — il ramo del dialetto non coperto, per esempio — la testa e' la prima diagnostica\r\n * ricevuta, perche' un blocco d'errore senza una prima riga costringerebbe a leggerle tutte\r\n * per capire di cosa si parla.\r\n */\r\n const leadIndex = Math.max(\r\n 0,\r\n diagnostics.findIndex((entry) => entry.code === DPE_CATALOG_UNAVAILABLE),\r\n );\r\n return {\r\n kind: 'Failed',\r\n lead: diagnostics[leadIndex],\r\n details: diagnostics.filter((_, index) => index !== leadIndex),\r\n hasError: diagnostics.some((entry) => entry.severity === 'Error'),\r\n };\r\n }\r\n const catalog = result.catalog;\r\n if (catalog && dpeFormulaCatalogSize(catalog) > 0) {\r\n return { kind: 'Palette', catalog };\r\n }\r\n return { kind: 'Silent', languageName: catalog?.languageName };\r\n}\r\n\r\n/**\r\n * L'etichetta con cui si nomina all'utente la forma pretesa dal campo.\r\n *\r\n * E' **traduzione**, non deduzione: la forma arriva dal backend, qui si sceglie solo come\r\n * chiamarla in italiano. Dedurla dal tipo di nodo sarebbe la regola di dominio che il contratto\r\n * vieta, e la differenza fra le due cose e' tutta in questa riga.\r\n */\r\nexport function dpeExpressionFormLabel(form: DpeExpressionForm | null | undefined): string | undefined {\r\n switch (form) {\r\n case 'Value':\r\n return 'valore';\r\n case 'Predicate':\r\n return 'predicato';\r\n case 'WindowFunction':\r\n return 'funzione di finestra';\r\n default:\r\n return undefined;\r\n }\r\n}\r\n\r\n/**\r\n * La stessa cosa detta all'utente: «serve un valore», «serve una funzione di finestra».\r\n *\r\n * Esiste separata dall'etichetta nuda per una ragione banale e reale: **l'articolo dipende dal\r\n * genere**, e comporre «serve un » + etichetta nel template produce «serve un funzione di\r\n * finestra». Il template non puo' sapere il genere di una parola, questa funzione si'.\r\n */\r\nexport function dpeExpressionFormPrompt(\r\n form: DpeExpressionForm | null | undefined,\r\n): string | undefined {\r\n switch (form) {\r\n case 'Value':\r\n return 'serve un valore';\r\n case 'Predicate':\r\n return 'serve un predicato';\r\n case 'WindowFunction':\r\n return 'serve una funzione di finestra';\r\n default:\r\n return undefined;\r\n }\r\n}\r\n\r\n/**\r\n * L'incrocio fra **la forma richiesta dal nodo** e **le forme che il compilatore sa compilare**.\r\n *\r\n * E' la meta' «disabilitare» della funzione che il contratto prescriveva: la meta' «mostrare» —\r\n * la pastiglia «serve una funzione di finestra» — era implementabile da LF6, perche' il primo\r\n * operando arriva con il catalogo; il secondo non stava sul filo, e senza di esso l'editor poteva\r\n * solo dire cosa serve, mai che non si potra' avere.\r\n *\r\n * Stessa forma di `DpeCommandAvailability` (`dpe-capabilities.ts`) e per la stessa ragione: un\r\n * campo spento che non dice perche' manda a cercare un guasto che non c'e'.\r\n */\r\nexport interface DpeExpressionFormSupport {\r\n readonly isSupported: boolean;\r\n /** Presente **solo** quando la forma non e' supportata. E' il testo che il campo mostra. */\r\n readonly reason?: string;\r\n}\r\n\r\n/** Il campo e' utilizzabile: l'assenza di informazioni cade qui, e non nell'altro ramo. */\r\nconst SUPPORTATA: DpeExpressionFormSupport = { isSupported: true };\r\n\r\n/**\r\n * La frase intera per ciascuna forma, e non «non sa compilare un » piu' l'etichetta.\r\n *\r\n * `frontend.md` §5-quinquies, normativo, e costato una volta: comporre un frammento fisso con un\r\n * nome che varia in genere produce «serve un funzione di finestra». L'italiano concorda, quindi\r\n * dove serve una frase la si scrive per intero.\r\n */\r\nconst FRASE_NON_SUPPORTATA: Record<DpeExpressionForm, string> = {\r\n Value: 'Campo disabilitato: il compilatore registrato non sa compilare un valore, che e’ la forma richiesta da questo nodo.',\r\n Predicate:\r\n 'Campo disabilitato: il compilatore registrato non sa compilare un predicato, che e’ la forma richiesta da questo nodo.',\r\n WindowFunction:\r\n 'Campo disabilitato: il compilatore registrato non sa compilare una funzione di finestra, che e’ la forma richiesta da questo nodo.',\r\n};\r\n\r\n/**\r\n * Decide se il campo formula si disabilita, e con quale motivo.\r\n *\r\n * Le tre regole, in quest'ordine e non intercambiabili:\r\n *\r\n * 1. **forma richiesta assente** -> supportato: il nodo non ospita formule, o la prima validazione\r\n * non e' ancora arrivata. Non c'e' niente da incrociare;\r\n * 2. **forme supportate non dichiarate** (`undefined` o `null`) -> supportato. E' la politica del\r\n * contratto: assente significa **non si disabilita niente**. Un ospite che non dichiara le\r\n * capacita' del proprio compilatore non autorizza l'editor a indovinarle, e un campo spento per\r\n * ignoranza toglie all'utente l'unica cosa che avrebbe: provare e leggere la risposta del\r\n * backend;\r\n * 3. **dichiarate e la forma non c'e'** -> non supportato. Un elenco **vuoto** entra qui, e ci\r\n * entra di proposito: vuoto non e' assente, e' un compilatore che dichiara di non compilare\r\n * nessuna forma.\r\n *\r\n * Nessuna di queste e' una regola di dominio: la forma la dice il backend, le capacita' pure.\r\n * Questa funzione fa solo l'intersezione e sceglie le parole italiane.\r\n */\r\nexport function dpeExpressionFormSupport(\r\n requiredForm: DpeExpressionForm | null | undefined,\r\n supportedForms: readonly DpeExpressionForm[] | null | undefined,\r\n): DpeExpressionFormSupport {\r\n if (!requiredForm || supportedForms == null) {\r\n return SUPPORTATA;\r\n }\r\n if (supportedForms.includes(requiredForm)) {\r\n return SUPPORTATA;\r\n }\r\n const supportate = supportedForms\r\n .map((form) => dpeExpressionFormLabel(form))\r\n .filter((label): label is string => label !== undefined);\r\n // L'elenco delle forme che restano: i nomi nudi qui stanno bene, sono un elenco e non una frase.\r\n const coda =\r\n supportate.length > 0\r\n ? ` Forme supportate: ${supportate.join(', ')}.`\r\n : ' Il compilatore non dichiara nessuna forma supportata.';\r\n return { isSupported: false, reason: FRASE_NON_SUPPORTATA[requiredForm] + coda };\r\n}\r\n\r\n/** Quante voci porta un catalogo. Zero e' l'esito «il compilatore non si descrive», non un guasto. */\r\nexport function dpeFormulaCatalogSize(catalog: DpeFormulaCatalog): number {\r\n return (\r\n (catalog.functions?.length ?? 0) +\r\n (catalog.operators?.length ?? 0) +\r\n (catalog.symbols?.length ?? 0)\r\n );\r\n}\r\n","/**\r\n * Il ponte fra il documento e il backend: valida in modo **debounced**, chiede l'anteprima SQL su\r\n * comando e salva su comando.\r\n *\r\n * Il salvataggio sta qui e non in un componente per la stessa ragione degli altri due: nessun\r\n * componente conosce il trasporto. Che il nome della classe sia piu' stretto della sua\r\n * responsabilita' e' un debito segnalato all'architect, non una svista.\r\n *\r\n * Perche' il debounce non e' un dettaglio: la chiamata al service DPE e' sincrona nel\r\n * backend, cioe' occupa il chiamante finche' non termina. Incatenarne una a ogni battitura\r\n * significa una coda di lavori che nessuno leggera' — e nel frattempo l'editor mostra un\r\n * risultato vecchio. Qui si aspetta la pausa di digitazione, si annulla la richiesta in volo\r\n * quando ne parte una nuova (`switchMap`), e si mostra sempre lo stato di attesa.\r\n *\r\n * L'editing resta **sincrono** mentre una validazione e' in volo: il documento e' la fonte di\r\n * verita', le diagnostiche arrivano dopo e non bloccano niente.\r\n *\r\n * Provider del componente radice come gli store: montare due editor non deve significare due\r\n * servizi che validano lo stesso documento.\r\n */\r\n\r\nimport { DestroyRef, Injectable, computed, inject, signal } from '@angular/core';\r\nimport { toObservable, takeUntilDestroyed } from '@angular/core/rxjs-interop';\r\nimport { EMPTY, catchError, debounceTime, filter, of, switchMap, tap } from 'rxjs';\r\n\r\nimport { DpeBuilderApi } from '../api/dpe-builder-api';\r\nimport { DpeApiError } from '../api/dpe-api-error';\r\nimport { DpeExpressionForm, DpeParameterValue } from '../model/dpe-api.model';\r\nimport { SqlDialectKind } from '../model/dpe-definition.model';\r\nimport { dpeReadCapabilities } from './dpe-capabilities';\r\nimport { DpeDocumentStore } from './dpe-document.store';\r\n\r\n/** Pausa di digitazione oltre la quale vale la pena chiedere al backend. */\r\nconst VALIDATE_DEBOUNCE_MS = 400;\r\n\r\n/**\r\n * Durata dell'avviso di salvataggio riuscito. Transitorio di proposito: il fatto durevole e'\r\n * «nessuna modifica» nell'indicatore, e un avviso di successo che resta va poi chiuso a mano.\r\n */\r\nconst SAVE_NOTICE_MS = 2600;\r\n\r\n@Injectable()\r\nexport class DpeValidationService {\r\n private readonly api = inject(DpeBuilderApi);\r\n private readonly store = inject(DpeDocumentStore);\r\n private readonly destroyRef = inject(DestroyRef);\r\n\r\n private readonly _isValidating = signal(false);\r\n private readonly _isCompiling = signal(false);\r\n private readonly _error = signal<DpeApiError | undefined>(undefined);\r\n /**\r\n * Le primitive opzionali si sanno **alla costruzione**, per identita' del metodo\r\n * (`dpe-capabilities.ts`, `contracts.md` §7). Prima si partiva da «disponibile» e si scopriva\r\n * l'assenza dal primo `MissingService`: il comando sembrava attivo e falliva alla pressione,\r\n * che e' peggio di un comando disabilitato che ne dice il motivo.\r\n */\r\n private readonly capabilities = dpeReadCapabilities(this.api);\r\n private readonly _isPreviewAvailable = signal(this.capabilities.compilePreview);\r\n private readonly _isSaving = signal(false);\r\n /** Come l'anteprima: un host senza `save` disabilita il comando, non rompe l'editor. */\r\n private readonly _isSaveAvailable = signal(this.capabilities.save);\r\n /** Esito dell'ultimo salvataggio: transitorio al successo, persistente al fallimento. */\r\n private readonly _justSaved = signal(false);\r\n private readonly _saveError = signal<DpeApiError | undefined>(undefined);\r\n /**\r\n * Il dialetto **dichiarato dall'ambiente** (ADR 0017), quando l'host espone `getEnvironment`.\r\n * Non e' il dialetto con cui si compila — quello sta nella definizione — ed e' esattamente per\r\n * questo che i due si tengono separati: il confronto e' l'unica cosa che l'editor puo' dire.\r\n */\r\n private readonly _environmentDialect = signal<SqlDialectKind | undefined>(undefined);\r\n /**\r\n * Il nome del **linguaggio di formule** dichiarato dall'ambiente (ADR 0031/0036). Serve\r\n * all'etichetta del campo espressione, e arriva da qui e non dal catalogo per una ragione\r\n * precisa: il catalogo puo' essere assente o nullo — compilatore che non descrive il proprio\r\n * linguaggio, dialetto non coperto — mentre il nome del linguaggio si sa sempre. Letto dal\r\n * catalogo, l'editor resterebbe muto proprio nei casi in cui serve.\r\n */\r\n private readonly _expressionLanguage = signal<string | undefined>(undefined);\r\n /** Il compilatore descrive il proprio linguaggio: se no, `getFormulaCatalog` non si chiama. */\r\n private readonly _providesFormulaCatalog = signal(false);\r\n /**\r\n * Le forme che il compilatore registrato sa produrre (ADR 0031), dall'ambiente. E' il **secondo\r\n * operando** dell'incrocio che disabilita un campo formula incompilabile; il primo,\r\n * `requiredForm`, e' per nodo e arriva con la **validazione**, su `DpeNodeSchemaInfo`.\r\n *\r\n * I due arrivano da due chiamate diverse, ed e' la ragione per cui questo campo sta qui: le forme\r\n * supportate sono dell'**installazione** e si chiedono una volta all'apertura, la forma richiesta\r\n * e' del **nodo** e si aggiorna a ogni validazione.\r\n *\r\n * `undefined` non e' «nessuna forma», e' «non dichiarate», e non disabilita niente: la\r\n * distinzione e' tutta la politica del contratto e per questo non si normalizza qui in un\r\n * elenco vuoto, che direbbe l'opposto.\r\n */\r\n private readonly _supportedForms = signal<readonly DpeExpressionForm[] | undefined>(undefined);\r\n\r\n readonly isValidating = this._isValidating.asReadonly();\r\n readonly isCompiling = this._isCompiling.asReadonly();\r\n readonly error = this._error.asReadonly();\r\n readonly isPreviewAvailable = this._isPreviewAvailable.asReadonly();\r\n readonly isSaving = this._isSaving.asReadonly();\r\n readonly isSaveAvailable = this._isSaveAvailable.asReadonly();\r\n readonly justSaved = this._justSaved.asReadonly();\r\n readonly saveError = this._saveError.asReadonly();\r\n readonly environmentDialect = this._environmentDialect.asReadonly();\r\n readonly expressionLanguage = this._expressionLanguage.asReadonly();\r\n readonly providesFormulaCatalog = this._providesFormulaCatalog.asReadonly();\r\n readonly supportedForms = this._supportedForms.asReadonly();\r\n\r\n /** Vero finche' non e' arrivata la prima risposta: la UI non deve dire \"nessun problema\". */\r\n readonly hasResult = computed(() => this.store.validateResult() !== undefined);\r\n\r\n constructor() {\r\n toObservable(this.store.definition)\r\n .pipe(\r\n /*\r\n * **Non si valida il segnaposto.** Lo store nasce con `dpeEmptyDefinition('', '', 'Sqlite')`\r\n * e questo servizio si iscrive alla costruzione: se il documento vero arriva dopo la\r\n * finestra di debounce — un ospite che monta l'editor mentre `load(nome)` e' in volo, cioe'\r\n * il caso normale con una rete vera — la finestra si chiudeva sul segnaposto e l'editor\r\n * validava un documento che nessuno aveva fornito. La risposta era esatta e inutile:\r\n * `DPE_MODEL_REQUIRED_MISSING` su `name`, `label` e `dataSources` piu'\r\n * `DPE_GRAPH_NO_WRITEBACK` — quattro rilievi del segnaposto, che l'utente non puo'\r\n * correggere da qui e che sembrano riguardare la sua definizione appena creata.\r\n *\r\n * Il filtro sta **prima** del debounce: il segnaposto non deve nemmeno aprire la finestra.\r\n */\r\n filter(() => this.store.hasDocument()),\r\n debounceTime(VALIDATE_DEBOUNCE_MS),\r\n tap(() => {\r\n this._isValidating.set(true);\r\n this._error.set(undefined);\r\n }),\r\n // `switchMap`: una definizione piu' recente rende inutile la risposta precedente, e\r\n // applicarla dopo mostrerebbe diagnostiche di un documento che non esiste piu'.\r\n switchMap((definition) =>\r\n this.api.validate(definition).pipe(\r\n tap((result) => {\r\n this.store.applyValidateResult(result);\r\n this._isValidating.set(false);\r\n }),\r\n catchError((error: unknown) => {\r\n this._isValidating.set(false);\r\n this._error.set(this.toApiError(error));\r\n // Le diagnostiche precedenti **restano**: cancellarle direbbe \"va tutto bene\"\r\n // proprio quando non lo sappiamo piu'.\r\n return EMPTY;\r\n }),\r\n ),\r\n ),\r\n takeUntilDestroyed(this.destroyRef),\r\n )\r\n .subscribe();\r\n\r\n // La dichiarazione dell'ambiente si chiede **una volta**, all'apertura: e' un fatto\r\n // dell'installazione, non cambia mentre si edita. Un guasto qui non si mostra come errore\r\n // dell'editor — senza dichiarazione l'editor si comporta come con `getEnvironment` assente.\r\n if (this.capabilities.getEnvironment) {\r\n this.api\r\n .getEnvironment()\r\n .pipe(\r\n tap((environment) => {\r\n this._environmentDialect.set(environment.dialect);\r\n // I due campi dell'ADR 0036 arrivano con la stessa risposta: sono fatti\r\n // dell'installazione come il dialetto, e chiederli a parte vorrebbe dire un secondo\r\n // giro di rete per sapere una cosa che si sa a tempo di configurazione.\r\n this._expressionLanguage.set(environment.expressionLanguage);\r\n this._providesFormulaCatalog.set(environment.providesFormulaCatalog === true);\r\n /*\r\n * `?? undefined` e non `?? []`: sul filo la proprieta' puo' arrivare assente **o**\r\n * nulla, e le due significano la stessa cosa — non dichiarate. Normalizzarle a un\r\n * elenco vuoto significherebbe l'opposto, cioe' «il compilatore non compila nessuna\r\n * forma», e disabiliterebbe **tutti** i campi formula di ogni ospite che non dichiara\r\n * le proprie capacita'.\r\n */\r\n this._supportedForms.set(environment.supportedForms ?? undefined);\r\n }),\r\n catchError(() => EMPTY),\r\n takeUntilDestroyed(this.destroyRef),\r\n )\r\n .subscribe();\r\n }\r\n\r\n // Il timer dell'avviso transitorio non deve sopravvivere all'editor: scriverebbe su un\r\n // signal di un componente distrutto.\r\n this.destroyRef.onDestroy(() => this.clearSaveNotice());\r\n }\r\n\r\n /** Anteprima dell'SQL. Opzionale sull'host: assente, il comando resta disabilitato. */\r\n requestPreview(parameterValues?: DpeParameterValue[]): void {\r\n if (!this._isPreviewAvailable()) {\r\n return;\r\n }\r\n this._isCompiling.set(true);\r\n this.api\r\n .compilePreview(this.store.definition(), parameterValues)\r\n .pipe(\r\n tap((result) => {\r\n this.store.applyCompileResult(result);\r\n this._isCompiling.set(false);\r\n }),\r\n catchError((error: unknown) => {\r\n this._isCompiling.set(false);\r\n const apiError = this.toApiError(error);\r\n if (apiError.category === 'MissingService') {\r\n this._isPreviewAvailable.set(false);\r\n } else {\r\n this._error.set(apiError);\r\n }\r\n return of(undefined);\r\n }),\r\n takeUntilDestroyed(this.destroyRef),\r\n )\r\n .subscribe();\r\n }\r\n\r\n /**\r\n * Salvataggio attraverso `DpeBuilderApi.save` (opzionale, `contracts.md` §7). Al successo\r\n * **l'editor dichiara da se'** lo stato salvato: quando l'ospite espone il metodo, e' questo il\r\n * punto che sa se e' andato a buon fine.\r\n *\r\n * Si passa a `markSaved` **il documento inviato**, non quello corrente: se l'utente scrive\r\n * mentre il salvataggio e' in volo, quelle modifiche non sono state salvate e l'indicatore deve\r\n * continuare a dirlo.\r\n */\r\n save(): void {\r\n if (!this._isSaveAvailable() || this._isSaving()) {\r\n return;\r\n }\r\n const sent = this.store.definition();\r\n this._isSaving.set(true);\r\n this._saveError.set(undefined);\r\n this.api\r\n .save(sent)\r\n .pipe(\r\n tap(() => {\r\n this._isSaving.set(false);\r\n // `markSaved` **solo** qui: un salvataggio fallito che azzerasse l'indicatore\r\n // cancellerebbe la traccia di un lavoro che non e' stato scritto da nessuna parte.\r\n this.store.markSaved(sent);\r\n this.showSaveNotice();\r\n }),\r\n catchError((error: unknown) => {\r\n this._isSaving.set(false);\r\n const apiError = this.toApiError(error);\r\n if (apiError.category === 'MissingService') {\r\n // Non dovrebbe accadere: la disponibilita' e' nota prima del clic. Se accade, l'host\r\n // ha sovrascritto la primitiva per rifiutarla — si disabilita il comando invece di\r\n // riproporlo a ogni tentativo.\r\n this._isSaveAvailable.set(false);\r\n }\r\n // L'esito si **vede**: banner persistente con la categoria. Un salvataggio che\r\n // fallisce in silenzio e' peggio di nessun salvataggio.\r\n this._saveError.set(apiError);\r\n return of(undefined);\r\n }),\r\n takeUntilDestroyed(this.destroyRef),\r\n )\r\n .subscribe();\r\n }\r\n\r\n dismissError(): void {\r\n this._error.set(undefined);\r\n }\r\n\r\n dismissSaveError(): void {\r\n this._saveError.set(undefined);\r\n }\r\n\r\n private saveNoticeTimer: ReturnType<typeof setTimeout> | undefined;\r\n\r\n private showSaveNotice(): void {\r\n this.clearSaveNotice();\r\n this._justSaved.set(true);\r\n this.saveNoticeTimer = setTimeout(() => {\r\n this.saveNoticeTimer = undefined;\r\n this._justSaved.set(false);\r\n }, SAVE_NOTICE_MS);\r\n }\r\n\r\n private clearSaveNotice(): void {\r\n if (this.saveNoticeTimer !== undefined) {\r\n clearTimeout(this.saveNoticeTimer);\r\n this.saveNoticeTimer = undefined;\r\n }\r\n this._justSaved.set(false);\r\n }\r\n\r\n /**\r\n * Un host che non usa `DpeApiError` non deve far esplodere l'editor: cio' che non e'\r\n * categorizzato diventa `Transport`, che e' la categoria \"riprovabile\".\r\n */\r\n private toApiError(error: unknown): DpeApiError {\r\n if (DpeApiError.is(error)) {\r\n return error;\r\n }\r\n const message = error instanceof Error ? error.message : 'Errore inatteso dal backend.';\r\n return new DpeApiError('Transport', message, error);\r\n }\r\n}\r\n","/**\r\n * Il catalogo del linguaggio di formule, **conservato per nodo** — `frontend.md` §5-quater,\r\n * normativo.\r\n *\r\n * `getFormulaCatalog` non e' una chiamata leggera: lato libreria costa una passata completa di\r\n * grafo e di propagazione, e interroga il provider di metadati per **tutti** i datasource della\r\n * definizione, non solo per il cono a monte del nodo. Chiederlo a ogni apertura di un campo formula\r\n * e' il modo naturale di scriverlo e quello sbagliato, e non lascia traccia: non fallisce, si\r\n * limita a far pagare al backend N volte una risposta che non e' cambiata.\r\n *\r\n * Quindi: **una volta per nodo**, conservato, e invalidato quando cambia cio' che puo' cambiarne il\r\n * contenuto.\r\n *\r\n * Cosa questo store **non** fa, e non e' una dimenticanza:\r\n *\r\n * - **non valida niente.** Il catalogo dice cosa esiste, non se cio' che l'utente ha scritto va\r\n * bene. La grammatica e' del compilatore dell'ospite, il frontend non la conosce e non puo'\r\n * conoscerla: nessuna validazione di formula viene scritta in TypeScript, per nessun motivo\r\n * (ADR 0031/0036, `contracts.md` §7);\r\n * - **non decide il nome del linguaggio.** Quello e' `DpeEnvironment.expressionLanguage`, e arriva\r\n * anche quando il catalogo non arriva — che e' il caso piu' importante dei due, perche' un\r\n * dialetto fuori da `SupportedDialects` rende il catalogo nullo (ADR 0031 §5) proprio mentre\r\n * l'utente ha ancora bisogno di sapere in che lingua sta scrivendo;\r\n * - **non chiama se non deve.** Servono due cose: la primitiva **sovrascritta e non dichiarata non\r\n * servita** (`dpe-capabilities.ts`) e il compilatore che dichiara di descrivere il proprio\r\n * linguaggio (`providesFormulaCatalog`). Un compilatore laconico risponde un catalogo vuoto, che\r\n * e' legittimo (ADR 0036 §5): farsi pagare una passata di grafo per riceverlo e' spreco puro.\r\n *\r\n * Provider del **componente radice** come gli altri store: due editor sulla stessa pagina hanno due\r\n * documenti, quindi due cache.\r\n */\r\n\r\nimport { Injectable, Signal, computed, effect, inject, signal, untracked } from '@angular/core';\r\nimport { catchError, of, tap } from 'rxjs';\r\n\r\nimport { DpeBuilderApi } from '../api/dpe-builder-api';\r\nimport {\r\n DpeFormulaCatalog,\r\n DpeFormulaCatalogResult,\r\n DpeNodeSchemaInfo,\r\n} from '../model/dpe-api.model';\r\nimport { dpeReadCapabilities } from './dpe-capabilities';\r\nimport { DpeDocumentStore } from './dpe-document.store';\r\nimport { DpeFormulaCatalogOutcome, dpeFormulaCatalogOutcome } from './dpe-formula-catalog.view';\r\nimport { DpeValidationService } from './dpe-validation.service';\r\n\r\n/**\r\n * L'impronta di cio' che puo' cambiare una risposta di `getFormulaCatalog`: **il nome del\r\n * documento** e **gli schemi che il backend ha propagato**.\r\n *\r\n * Le due parti corrispondono a cio' che la risposta contiene, e nessuna e' un indovinello: i\r\n * simboli `Column` *sono* le colonne che quel nodo vede, e quali siano lo decide la propagazione\r\n * dello schema, che e' del backend (**I4**). Qualunque altra impronta — «e' cambiata la topologia»,\r\n * «e' cambiato un alias di sorgente» — richiederebbe di sapere *quali* modifiche cambiano uno\r\n * schema, cioe' di riscrivere I4 in TypeScript.\r\n *\r\n * Conseguenza voluta, ed e' la ragione per cui questa funzione e' pura e verificabile: scrivere\r\n * nel testo di una formula **non** cambia l'impronta, quindi non invalida nulla. Rinominare un\r\n * alias a monte la cambia, e il catalogo si rilegge alla prossima apertura del nodo.\r\n *\r\n * Il nome del documento entra perche' prima della prima validazione gli schemi sono vuoti: senza\r\n * di esso due documenti diversi appena aperti avrebbero la stessa impronta, e il secondo\r\n * erediterebbe il catalogo del primo.\r\n *\r\n * **Qui c'era una terza parte, la forma dei nodi, e non c'e' piu'.** Era entrata quando la risposta\r\n * portava `requiredForm`, che dipende dal tipo del nodo mentre lo schema in uscita no: cambiare una\r\n * trasformazione da `Expression` a `ComputeRelative` lasciava l'impronta identica e la pastiglia\r\n * diceva la forma vecchia. Ora la forma arriva con la validazione e non con questa risposta, e\r\n * l'ingresso non esiste piu': misurato sui due lati, il contratto — il compilatore riceve\r\n * `DpeExpressionCatalogRequest`, cioe' **dialetto, colonne a monte e parametri**, e non il tipo del\r\n * nodo — e il browser, dove lo stesso nodo prima e dopo il cambio di tipo riceve un catalogo\r\n * identico (le stesse 32 voci). Toglierla non e' una semplificazione: e' non pagare una richiesta\r\n * per una risposta che non puo' essere cambiata.\r\n *\r\n * **Due ingressi restano scoperti, e sono scoperti da sempre:** il `dialect` e i `parameters` della\r\n * definizione, che pure entrano nella richiesta al compilatore. Oggi nessuno dei due si puo'\r\n * modificare a documento aperto — l'editor non li scrive, e caricarne un altro azzera gli schemi,\r\n * quindi l'impronta si muove lo stesso. Il giorno in cui compare un editor dei parametri, il posto\r\n * da cui ripartire e' questo.\r\n */\r\nexport function dpeFormulaCatalogSignature(\r\n definitionName: string,\r\n schemas: readonly DpeNodeSchemaInfo[],\r\n): string {\r\n const parts = schemas.map(\r\n (schema) =>\r\n // Nessun `?? []`: `columns` e' obbligatoria nel contratto, e il ripiego avrebbe nascosto una\r\n // risposta fuori contratto sotto lo stesso valore di una collezione legittimamente vuota.\r\n `${schema.nodeName}:${schema.columns\r\n .map((column) => `${column.name}/${column.type.kind}`)\r\n .join(',')}`,\r\n );\r\n // `\\u0000` come separatore: non puo' comparire in un nome di nodo ne' di colonna, quindi due\r\n // impronte diverse non possono collidere per concatenazione.\r\n return `${definitionName}\\u0000${parts.join('|')}`;\r\n}\r\n\r\n/**\r\n * Una voce di cache: **l'involucro intero** e l'impronta con cui e' stato ottenuto.\r\n *\r\n * L'involucro e non il catalogo: un esito con diagnostiche e' una **risposta**, non un guasto, e\r\n * conservare il solo catalogo ne buttava via la meta' che spiega perche' non c'e'. Era il difetto\r\n * misurato in LF6 — dialetto non coperto e backend rotto indistinguibili — e si chiude qui, dove\r\n * la risposta entra.\r\n */\r\ninterface DpeCatalogEntry {\r\n readonly signature: string;\r\n readonly result: DpeFormulaCatalogResult;\r\n}\r\n\r\n@Injectable()\r\nexport class DpeFormulaCatalogStore {\r\n private readonly api = inject(DpeBuilderApi);\r\n private readonly document = inject(DpeDocumentStore);\r\n /**\r\n * La dichiarazione dell'ambiente si chiede **una volta all'apertura**, e chi la chiede e'\r\n * `DpeValidationService`. Rileggerla da qui sarebbe un secondo `getEnvironment` per la stessa\r\n * risposta — e per giunta due risposte che possono arrivare in ordine diverso.\r\n */\r\n private readonly validation = inject(DpeValidationService);\r\n\r\n private readonly capabilities = dpeReadCapabilities(this.api);\r\n\r\n private readonly _byNode = signal<ReadonlyMap<string, DpeCatalogEntry>>(new Map());\r\n private readonly _pending = signal<ReadonlySet<string>>(new Set());\r\n /**\r\n * Il nodo di cui si vuole il catalogo — **uno**, quello aperto.\r\n *\r\n * Esiste per un difetto trovato nel browser e non da nessun test: la disponibilita' dipende da\r\n * `getEnvironment`, che e' una chiamata di rete, e chi apre un nodo subito dopo aver aperto\r\n * l'editor chiede il catalogo **prima** che l'ambiente abbia risposto. La richiesta cadeva nel\r\n * vuoto e non veniva piu' ripetuta, perche' l'apertura del nodo era l'unico innesco: il campo\r\n * restava senza catalogo per sempre, senza errori, e riaprire lo stesso nodo non cambiava\r\n * niente. Tenere qui **l'intenzione** invece di agire subito la rende ripetibile quando la\r\n * condizione che mancava si verifica.\r\n */\r\n private readonly _wanted = signal<string | undefined>(undefined);\r\n\r\n /**\r\n * Il catalogo si puo' chiedere: primitiva disponibile **e** compilatore che dichiara di\r\n * descrivere il proprio linguaggio. Le due meta' restano separate perche' sono due fatti\r\n * diversi — il trasporto e il compilatore — e un solo booleano non permetterebbe di capire\r\n * quale dei due manca.\r\n */\r\n readonly isAvailable: Signal<boolean> = computed(\r\n () => this.capabilities.getFormulaCatalog && this.validation.providesFormulaCatalog(),\r\n );\r\n\r\n /**\r\n * L'impronta corrente: cambia quando cambia cio' che puo' cambiare una risposta, cioe' gli\r\n * **schemi** che il backend ha propagato — sono loro a decidere i simboli.\r\n *\r\n * Legge il documento, e va bene: il valore prodotto e' una stringa, e a parita' di stringa un\r\n * `computed` non notifica nessuno. Scrivere in una formula ricalcola l'impronta e non muove\r\n * niente — cioe' nessuna richiesta a ogni battitura, che resta la regola di `frontend.md`\r\n * §5-quater.\r\n */\r\n readonly signature = computed(() =>\r\n dpeFormulaCatalogSignature(this.document.definition().name ?? '', this.document.schemas()),\r\n );\r\n\r\n /**\r\n * La prima validazione e' tornata, cioe' **gli schemi si conoscono**.\r\n *\r\n * Prima di allora il catalogo non si chiede, e non e' prudenza: i simboli `Column` sono le colonne\r\n * che il nodo vede, e chiederli quando gli schemi non sono ancora arrivati significa ricevere una\r\n * risposta che l'istante dopo e' scaduta — cioe' **due** chiamate per la stessa apertura, dove\r\n * ogni chiamata costa al backend una passata completa di grafo e di metadati. Misurato: aprire un\r\n * nodo subito dopo l'editor faceva salire il contatore di due.\r\n */\r\n private readonly hasValidated = computed(() => this.document.validateResult() !== undefined);\r\n\r\n /**\r\n * L'**involucro** noto per un nodo, se e' stato ottenuto con l'impronta corrente.\r\n *\r\n * Legge i signal, quindi si usa dentro un `computed` e resta reattivo — lo stesso schema di\r\n * `DpeMetadataStore.stateOf`.\r\n *\r\n * **Scade con l'impronta**: un catalogo ottenuto quando gli schemi erano altri non si restituisce\r\n * come se fosse attuale — dichiarerebbe disponibili simboli che non esistono piu'. Finche' il nodo\r\n * resta quello aperto, l'effect del costruttore ne chiede subito uno nuovo, quindi la scadenza si\r\n * vede al piu' come una sparizione momentanea del conteggio, non come un campo che resta muto.\r\n *\r\n * Scade anche l'esito d'errore, ed e' giusto: un `DPE_CATALOG_UNAVAILABLE` nasce da uno schema\r\n * che non si risolve, e cambiare gli schemi e' esattamente cio' che puo' averlo risolto.\r\n */\r\n resultOf(nodeName: string): DpeFormulaCatalogResult | undefined {\r\n const entry = this._byNode().get(nodeName);\r\n return entry && entry.signature === this.signature() ? entry.result : undefined;\r\n }\r\n\r\n /**\r\n * Il catalogo consegnato, **quando e' stato prodotto**. Puo' legittimamente essere vuoto: chi\r\n * rende non deve confondere «vuoto» con «assente», e per questo la decisione di cosa mostrare\r\n * passa da `outcomeOf` e non da qui.\r\n */\r\n catalogOf(nodeName: string): DpeFormulaCatalog | undefined {\r\n return this.resultOf(nodeName)?.catalog ?? undefined;\r\n }\r\n\r\n /**\r\n * Quale dei tre esiti del contratto si sta guardando — piu' «non lo so ancora».\r\n *\r\n * E' cio' che i componenti consumano: la tricotomia si legge **una volta**, in una funzione\r\n * pura, e non si riscrive in ogni ispettore con un `if` leggermente diverso.\r\n */\r\n outcomeOf(nodeName: string): DpeFormulaCatalogOutcome {\r\n return dpeFormulaCatalogOutcome(this.resultOf(nodeName));\r\n }\r\n\r\n /*\r\n * `requiredFormOf` **non sta piu' qui**, e non e' un trasloco di comodo: la forma arriva con lo\r\n * schema del nodo (`DpeNodeSchemaInfo.requiredForm`), quindi chiederla a questo store\r\n * significherebbe far dipendere dal catalogo un dato che col catalogo non c'entra — e farle\r\n * ereditare le due proprieta' sbagliate di una voce di cache, cioe' esserci solo se la rotta e'\r\n * stata chiamata e scadere quando scade il catalogo. Si legge da `DpeDocumentStore.schemaByNode`,\r\n * dove stanno gia' colonne e compilabilita' dello stesso record.\r\n */\r\n\r\n /** Una richiesta per questo nodo e' in volo: lo stato di attesa si mostra sempre. */\r\n isLoading(nodeName: string): boolean {\r\n return this._pending().has(nodeName);\r\n }\r\n\r\n constructor() {\r\n /*\r\n * L'innesco della richiesta, e dipende da **tre** signal: il nodo che si vuole, la\r\n * disponibilita', l'impronta. Nient'altro — in particolare **non** il documento: leggere qui\r\n * `document.definition()` significherebbe una richiesta di catalogo a ogni battitura, cioe' una\r\n * passata di grafo e di metadati al backend per ogni carattere digitato (`frontend.md`\r\n * §5-quater). L'impronta cambia solo quando cambiano gli schemi che il backend ha propagato,\r\n * quindi scrivere dentro una formula non la muove.\r\n *\r\n * Le altre due dipendenze chiudono due difetti trovati **nel browser**, non da un test:\r\n *\r\n * - la **disponibilita'** arriva da `getEnvironment`, cioe' dalla rete: chi apre un nodo subito\r\n * dopo l'editor chiedeva il catalogo prima che si sapesse se chiederlo, e la richiesta cadeva\r\n * nel vuoto per sempre;\r\n * - l'**impronta** cambia quando la prima validazione risponde, che e' un istante dopo\r\n * l'apertura del nodo. Il catalogo chiesto prima risultava registrato con l'impronta\r\n * precedente, cioe' **scaduto appena nato**: la chiamata partiva, la risposta arrivava, e il\r\n * campo non mostrava niente. Il contatore diceva 1 e lo schermo diceva zero. Da qui anche la\r\n * guardia su `hasValidated`: chiedere prima di sapere gli schemi costa **due** chiamate per\r\n * una sola apertura, e il rimedio giusto e' aspettare, non rileggere.\r\n */\r\n effect(() => {\r\n const nodeName = this._wanted();\r\n const signature = this.signature();\r\n if (!nodeName || !this.isAvailable() || !this.hasValidated()) {\r\n return;\r\n }\r\n untracked(() => this.fetchIfNeeded(nodeName, signature));\r\n });\r\n }\r\n\r\n /**\r\n * Chiede il catalogo per un nodo, **se serve**. Idempotente: chiamarla a ogni apertura della\r\n * finestra non produce chiamate, chiamarla in un ciclo nemmeno.\r\n *\r\n * Fa **due** cose, e servono entrambe:\r\n *\r\n * 1. prova subito, sotto `untracked`, cosi' l'esito e' immediato quando le condizioni ci sono\r\n * gia'. Affidarsi al solo `_wanted` non basterebbe: riaperto lo **stesso** nodo il signal non\r\n * cambia valore e l'effect non riparte;\r\n * 2. registra l'intenzione in `_wanted`, cosi' quando cambia una delle condizioni che mancavano\r\n * — la disponibilita' dell'ambiente, l'impronta degli schemi — la richiesta viene onorata\r\n * senza che l'utente debba riaprire il nodo.\r\n *\r\n * **Ogni lettura di signal e' in `untracked`**, e non e' prudenza: i chiamanti sono `effect` degli\r\n * ispettori, e un `this.document.definition()` letto qui diventerebbe una dipendenza di\r\n * quell'effect — cioe' una richiesta di catalogo **a ogni battitura**, che e' il difetto che\r\n * questo store esiste per evitare. Il sintomo non sarebbe un errore: sarebbe un backend che\r\n * macina grafi mentre qualcuno scrive.\r\n */\r\n requestFor(nodeName: string): void {\r\n if (!nodeName) {\r\n return;\r\n }\r\n this._wanted.set(nodeName);\r\n untracked(() => {\r\n if (this.isAvailable() && this.hasValidated()) {\r\n this.fetchIfNeeded(nodeName, this.signature());\r\n }\r\n });\r\n }\r\n\r\n /**\r\n * La chiamata, **se serve davvero**: non c'e' gia' una richiesta in volo per quel nodo, e cio'\r\n * che e' in cache non e' stato ottenuto con l'impronta corrente.\r\n *\r\n * Tutte le letture stanno sotto `untracked` da parte dei chiamanti: sono signal, e legarli\r\n * all'effect vorrebbe dire rileggere il catalogo a ogni modifica del documento.\r\n */\r\n private fetchIfNeeded(nodeName: string, signature: string): void {\r\n if (this._pending().has(nodeName)) {\r\n return;\r\n }\r\n const known = this._byNode().get(nodeName);\r\n if (known && known.signature === signature) {\r\n return;\r\n }\r\n this.fetch(nodeName, signature, this.document.definition());\r\n }\r\n\r\n /**\r\n * Rilettura forzata, per chi vuole il catalogo aggiornato senza aspettare un cambio di impronta.\r\n * Non ha un consumatore nell'editor di oggi ed esiste perche' una cache senza modo di essere\r\n * invalidata a mano e' una cache che prima o poi si aggira con un ricaricamento della pagina.\r\n */\r\n refresh(nodeName: string): void {\r\n untracked(() => {\r\n if (!this.isAvailable() || this._pending().has(nodeName)) {\r\n return;\r\n }\r\n this.fetch(nodeName, this.signature(), this.document.definition());\r\n });\r\n }\r\n\r\n\r\n private fetch(\r\n nodeName: string,\r\n signature: string,\r\n definition: Parameters<DpeBuilderApi['getFormulaCatalog']>[0],\r\n ): void {\r\n this.setPending(nodeName, true);\r\n this.api\r\n .getFormulaCatalog(definition, nodeName)\r\n .pipe(\r\n tap((result) => {\r\n this.setPending(nodeName, false);\r\n /*\r\n * Si registra con l'impronta **della richiesta**, non con quella corrente: se il\r\n * documento e' cambiato mentre la risposta era in volo, questa voce risulta subito\r\n * scaduta e verra' riletta alla prossima apertura. L'alternativa — registrarla come\r\n * fresca — spaccerebbe per attuale un catalogo calcolato su un documento precedente.\r\n */\r\n this.put(nodeName, { signature, result });\r\n /*\r\n * E se nel frattempo l'impronta si e' mossa, si rilegge **subito**.\r\n *\r\n * Senza questa riga il caso normale non funziona, ed e' il difetto piu' caro del lotto:\r\n * la prima validazione risponde un istante dopo l'apertura della finestra, quindi mentre\r\n * la richiesta e' in volo l'impronta cambia. L'effect riparte, trova la richiesta\r\n * `pending` e rinuncia — correttamente, non si duplicano le chiamate — e la risposta che\r\n * arriva subito dopo viene registrata con l'impronta **precedente**, cioe' scaduta appena\r\n * nata. Esito a schermo: il contatore diceva 1 e il campo non mostrava niente.\r\n */\r\n untracked(() => this.fetchIfNeeded(nodeName, this.signature()));\r\n }),\r\n catchError(() => {\r\n this.setPending(nodeName, false);\r\n /*\r\n * Un guasto **di trasporto** non si registra: niente in cache significa «non lo so», e\r\n * la prossima apertura riprova. Non si mostra come errore dell'editor — senza catalogo\r\n * il campo formula e' un campo di testo, che e' cio' che era prima dell'ADR 0036, e\r\n * l'etichetta del linguaggio arriva comunque dall'ambiente.\r\n *\r\n * Da non confondere con un esito **con diagnostiche**, che passa dal ramo sopra: quello\r\n * e' una risposta, si conserva e si mostra. Sono le due cose che prima arrivavano qui\r\n * insieme, e distinguerle e' il motivo per cui la rotta consegna l'involucro.\r\n */\r\n return of(undefined);\r\n }),\r\n )\r\n .subscribe();\r\n }\r\n\r\n private put(nodeName: string, entry: DpeCatalogEntry): void {\r\n const next = new Map(this._byNode());\r\n next.set(nodeName, entry);\r\n this._byNode.set(next);\r\n }\r\n\r\n private setPending(nodeName: string, pending: boolean): void {\r\n const next = new Set(this._pending());\r\n if (pending) {\r\n next.add(nodeName);\r\n } else {\r\n next.delete(nodeName);\r\n }\r\n this._pending.set(next);\r\n }\r\n}\r\n","/**\r\n * Layout automatico del DAG con dagre.\r\n *\r\n * Due cose non ovvie, ed entrambe sono il motivo per cui esiste questo involucro invece di\r\n * chiamare dagre dai componenti:\r\n *\r\n * 1. **dagre ragiona in centri**, il canvas vuole l'angolo in alto a sinistra. La conversione\r\n * sta qui, una volta: fatta a valle, in ogni chiamante, sbaglia di mezzo nodo e il\r\n * disallineamento si nota solo a occhio;\r\n * 2. **dagre non e' ESM**: sta in `allowedCommonJsDependencies` di `angular.json`, altrimenti\r\n * la build avvisa a ogni compilazione.\r\n *\r\n * Input minimale di proposito: nomi e misure, piu' gli archi. Il servizio non conosce il\r\n * documento e non lo scrive — restituisce le posizioni, e chi lo invoca decide cosa farne.\r\n */\r\n\r\nimport { Injectable } from '@angular/core';\r\nimport dagre from 'dagre';\r\n\r\nexport interface DpeLayoutNodeInput {\r\n name: string;\r\n width: number;\r\n height: number;\r\n}\r\n\r\nexport interface DpeLayoutEdgeInput {\r\n /** Nome del nodo a monte. */\r\n from: string;\r\n /** Nome del nodo a valle. */\r\n to: string;\r\n /** Identificatore dell'arco: dagre ne ha bisogno per ammettere archi paralleli. */\r\n id: string;\r\n}\r\n\r\nexport interface DpeLayoutPosition {\r\n x: number;\r\n y: number;\r\n}\r\n\r\nexport type DpeLayoutDirection = 'TB' | 'LR';\r\n\r\nexport interface DpeLayoutOptions {\r\n /** Default `TB`: i dati scendono dalle sorgenti ai sink. */\r\n direction?: DpeLayoutDirection;\r\n /** Distanza fra i livelli. */\r\n rankSeparation?: number;\r\n /** Distanza fra i nodi dello stesso livello. */\r\n nodeSeparation?: number;\r\n /** Angolo in alto a sinistra del grafo risultante. */\r\n origin?: DpeLayoutPosition;\r\n}\r\n\r\n@Injectable()\r\nexport class DpeLayoutService {\r\n /**\r\n * Calcola le posizioni. La chiave della mappa e' il **nome** del nodo, non il suo id sul\r\n * canvas: il nome e' cio' che identifica un nodo nel documento, e passare dall'id\r\n * costringerebbe questo servizio a conoscere la costruzione degli id.\r\n */\r\n layout(\r\n nodes: readonly DpeLayoutNodeInput[],\r\n edges: readonly DpeLayoutEdgeInput[],\r\n options?: DpeLayoutOptions,\r\n ): Map<string, DpeLayoutPosition> {\r\n const origin = options?.origin ?? { x: 60, y: 60 };\r\n\r\n const graph = new dagre.graphlib.Graph({ multigraph: true, compound: false });\r\n graph.setGraph({\r\n rankdir: options?.direction ?? 'TB',\r\n ranksep: options?.rankSeparation ?? 90,\r\n nodesep: options?.nodeSeparation ?? 60,\r\n marginx: 0,\r\n marginy: 0,\r\n });\r\n graph.setDefaultEdgeLabel(() => ({}));\r\n\r\n const known = new Set<string>();\r\n for (const node of nodes) {\r\n graph.setNode(node.name, { width: node.width, height: node.height });\r\n known.add(node.name);\r\n }\r\n\r\n for (const edge of edges) {\r\n // Un arco con un capo sconosciuto e' un riferimento rotto: si disegna comunque sul\r\n // canvas, ma non vincola il layout — dagre creerebbe un nodo fantasma con misure zero.\r\n if (!known.has(edge.from) || !known.has(edge.to)) {\r\n continue;\r\n }\r\n graph.setEdge(edge.from, edge.to, {}, edge.id);\r\n }\r\n\r\n dagre.layout(graph);\r\n\r\n const positions = new Map<string, DpeLayoutPosition>();\r\n let minX = Number.POSITIVE_INFINITY;\r\n let minY = Number.POSITIVE_INFINITY;\r\n\r\n for (const node of nodes) {\r\n const laid = graph.node(node.name) as { x?: number; y?: number } | undefined;\r\n if (!laid || laid.x === undefined || laid.y === undefined) {\r\n continue;\r\n }\r\n const x = laid.x - node.width / 2;\r\n const y = laid.y - node.height / 2;\r\n positions.set(node.name, { x, y });\r\n minX = Math.min(minX, x);\r\n minY = Math.min(minY, y);\r\n }\r\n\r\n // Trasla il grafo sull'origine scelta: dagre puo' produrre coordinate negative, e un nodo\r\n // a coordinate negative finisce fuori dall'area visibile appena il canvas si apre.\r\n if (Number.isFinite(minX) && Number.isFinite(minY)) {\r\n const deltaX = origin.x - minX;\r\n const deltaY = origin.y - minY;\r\n for (const [name, position] of positions) {\r\n positions.set(name, {\r\n x: Math.round(position.x + deltaX),\r\n y: Math.round(position.y + deltaY),\r\n });\r\n }\r\n }\r\n\r\n return positions;\r\n }\r\n}\r\n","/**\r\n * Cache locale del provider di metadati dell'host.\r\n *\r\n * Tabelle e campi arrivano da `DpeBuilderApi`, cioe' dal **provider dell'host** e non dai\r\n * cataloghi del database (ADR 0004). Due cose che questa cache non fa, di proposito:\r\n *\r\n * - **non appiattisce i tre esiti**. `Declared`, `DeclaredWithoutFields` (\"la tabella c'e', i\r\n * campi non li so\") e `Unknown` restano distinti fino alla UI: chi vede una tendina vuota\r\n * deve poter sapere quale dei tre casi sta guardando, perche' nel secondo il nome del campo\r\n * si digita a mano e non c'e' niente da correggere;\r\n * - **non giudica**. Se una tabella non e' nel catalogo, il rilievo autorevole\r\n * (`DPE_REF_UNKNOWN_TABLE`) arriva dalla validazione; qui si sa solo che non si sa.\r\n *\r\n * L'elenco delle tabelle puo' essere grande: si chiede una volta, si tiene, e chi lo mostra\r\n * filtra invece di riversarlo in un `<select>`.\r\n */\r\n\r\nimport { Injectable, inject, signal } from '@angular/core';\r\nimport { catchError, of, tap } from 'rxjs';\r\n\r\nimport { DpeBuilderApi } from '../api/dpe-builder-api';\r\nimport { TableDescriptor, TableLookupOutcome, TableSummary } from '../model/dpe-api.model';\r\n\r\nexport interface DpeTableState {\r\n outcome: TableLookupOutcome;\r\n table?: TableDescriptor;\r\n}\r\n\r\n@Injectable()\r\nexport class DpeMetadataStore {\r\n private readonly api = inject(DpeBuilderApi);\r\n\r\n private readonly _tables = signal<readonly TableSummary[]>([]);\r\n private readonly _isLoadingTables = signal(false);\r\n private readonly _tablesRequested = signal(false);\r\n private readonly _byName = signal<ReadonlyMap<string, DpeTableState>>(new Map());\r\n private readonly pending = new Set<string>();\r\n\r\n readonly tables = this._tables.asReadonly();\r\n readonly isLoadingTables = this._isLoadingTables.asReadonly();\r\n\r\n /** Idempotente: la prima chiamata carica, le successive non fanno nulla. */\r\n loadTables(): void {\r\n if (this._tablesRequested()) {\r\n return;\r\n }\r\n this._tablesRequested.set(true);\r\n this._isLoadingTables.set(true);\r\n this.api\r\n .getTables()\r\n .pipe(\r\n tap((tables) => {\r\n this._tables.set(tables);\r\n this._isLoadingTables.set(false);\r\n }),\r\n catchError(() => {\r\n // Elenco vuoto significa \"non lo so\", non \"nessuna tabella\": la UI lascia digitare\r\n // il nome invece di impedire di scriverlo.\r\n this._isLoadingTables.set(false);\r\n return of([]);\r\n }),\r\n )\r\n .subscribe();\r\n }\r\n\r\n /** Stato noto di una tabella, se gia' richiesta. */\r\n stateOf(tableName: string): DpeTableState | undefined {\r\n return this._byName().get(tableName);\r\n }\r\n\r\n /** Chiede la descrizione di una tabella una sola volta per nome. */\r\n describe(tableName: string): void {\r\n if (!tableName || this.pending.has(tableName) || this._byName().has(tableName)) {\r\n return;\r\n }\r\n this.pending.add(tableName);\r\n this.api\r\n .getTable(tableName)\r\n .pipe(\r\n tap((result) => {\r\n this.pending.delete(tableName);\r\n this.put(tableName, { outcome: result.outcome, table: result.table });\r\n }),\r\n catchError(() => {\r\n this.pending.delete(tableName);\r\n // Un guasto di trasporto non e' `Unknown`: `Unknown` e' una risposta del provider.\r\n // Non si registra nulla, cosi' un tentativo successivo riprova.\r\n return of(undefined);\r\n }),\r\n )\r\n .subscribe();\r\n }\r\n\r\n private put(tableName: string, state: DpeTableState): void {\r\n const next = new Map(this._byName());\r\n next.set(tableName, state);\r\n this._byName.set(next);\r\n }\r\n}\r\n","/**\r\n * I tracciati SVG delle icone di categoria, uno per collezione di nodi.\r\n *\r\n * **Perche' tracciati e non glifi Unicode** (divergenza motivata dal progetto di riferimento,\r\n * `frontend.md` §3-ter): i glifi non hanno metriche ne' linea di base coerenti fra loro e su\r\n * alcune piattaforme diventano emoji a colori — cioe' proprio la resa sbagliata quando stanno\r\n * in colonna in una palette. Un tracciato disegnato su una griglia 24x24 con `currentColor` si\r\n * colora con una sola regola CSS e vale identico in tema chiaro e scuro.\r\n *\r\n * **Nessun asset e nessuna dipendenza**: sono stringhe, e il componente che le rende le lega con\r\n * `[attr.d]` senza mai toccare `innerHTML` — quindi niente `bypassSecurityTrust*`.\r\n *\r\n * Il disegno e' pensato per la resa a **16px**: pochi tratti, nessun dettaglio sotto i 2 px di\r\n * griglia, e sagome che restano distinguibili l'una dall'altra quando sono incolonnate.\r\n */\r\n\r\nimport { DpeNodeCollectionName } from '../model/dpe-definition.model';\r\nimport { dpeNodeCategory } from './dpe-node-catalog';\r\n\r\n/**\r\n * La famiglia cromatica di una categoria. **Non e' una tassonomia nuova**: si deriva per intero\r\n * da `isSource`/`isSink`/`isNotCompilableInV1` del catalogo, che restano l'unica descrizione dei\r\n * tipi di nodo. Serve solo a scegliere un colore.\r\n */\r\nexport type DpeNodeIconFamily = 'source' | 'transform' | 'sink' | 'uncompilable';\r\n\r\nexport function dpeNodeIconFamily(collection: DpeNodeCollectionName): DpeNodeIconFamily {\r\n const category = dpeNodeCategory(collection);\r\n if (category.isSource) {\r\n return 'source';\r\n }\r\n if (category.isSink) {\r\n return 'sink';\r\n }\r\n // Dopo sorgenti e scritture: un sink non compilabile non esiste, e se un giorno esistesse\r\n // resterebbe piu' utile vederlo come sink.\r\n if (category.isNotCompilableInV1) {\r\n return 'uncompilable';\r\n }\r\n return 'transform';\r\n}\r\n\r\n/** Classe CSS della famiglia: un solo posto la costruisce, cosi' i fogli restano allineati. */\r\nexport function dpeNodeIconFamilyClass(collection: DpeNodeCollectionName): string {\r\n return `dpe-family--${dpeNodeIconFamily(collection)}`;\r\n}\r\n\r\n/**\r\n * I tracciati, per collezione. Ogni voce e' l'elenco degli attributi `d` di un `<path>`; il\r\n * contorno e' `stroke`, mai `fill`, cosi' l'icona pesa uguale su qualunque sfondo.\r\n */\r\nexport const DPE_NODE_ICON_PATHS: Readonly<Record<DpeNodeCollectionName, readonly string[]>> = {\r\n // Cilindro di database: ellisse superiore, fianchi, e la riga di mezzo che lo fa leggere\r\n // come un solido invece che come un ovale con due gambe.\r\n dataSources: [\r\n 'M4 6a8 3 0 1 0 16 0a8 3 0 1 0 -16 0',\r\n 'M4 6v12c0 1.66 3.58 3 8 3s8-1.34 8-3V6',\r\n 'M4 12c0 1.66 3.58 3 8 3s8-1.34 8-3',\r\n ],\r\n // Imbuto.\r\n filters: ['M3 4h18l-7 8.5V20l-4-2v-5.5z'],\r\n // Due cerchi che si intersecano: ogni cerchio e' due archi da 180 gradi. I centri stanno a 7\r\n // unita' e il raggio e' 4.5: con una sovrapposizione maggiore, a 16px, si leggevano come un\r\n // cerchio con un anello dentro invece che come due insiemi che si incrociano.\r\n joins: ['M8.5 7.5a4.5 4.5 0 1 0 0 9 4.5 4.5 0 1 0 0-9', 'M15.5 7.5a4.5 4.5 0 1 0 0 9 4.5 4.5 0 1 0 0-9'],\r\n // Tre barre di altezza diversa. Rettangoli e non segmenti: a 16px un segmento verticale\r\n // spesso 2 non si distingue da una barra sottile, e l'insieme si confonde con l'unione.\r\n aggregates: ['M4 11h4v9H4z', 'M10 5h4v15h-4z', 'M16 14h4v6h-4z'],\r\n // Due frecce affiancate in versi opposti.\r\n transforms: ['M3 8h13', 'M12 4l4 4-4 4', 'M21 16H8', 'M12 12l-4 4 4 4'],\r\n // Due lastre impilate.\r\n appends: ['M4 5h16v5H4z', 'M4 14h16v5H4z'],\r\n // Albero: un nodo in alto, due sotto, collegati.\r\n hierarchyPaths: ['M9 3h6v4H9z', 'M12 7v3', 'M6 10h12', 'M6 10v3', 'M18 10v3', 'M3 13h6v4H3z', 'M15 13h6v4h-6z'],\r\n // Spezzata crescente, con la punta che dichiara la direzione.\r\n forecasts: ['M21 7l-8 8-4-4-6 6', 'M15 7h6v6'],\r\n // Tassello di puzzle: una bozza sopra, un incavo a destra. Due sole sporgenze e non quattro —\r\n // a 16px il tassello \"completo\" diventava un quadrato con quattro tacche indistinguibili.\r\n customNodes: ['M4 4h5.5a2.5 2.5 0 0 1 5 0H20v5.5a2.5 2.5 0 0 0 0 5V20H4z'],\r\n // Freccia che entra in un contenitore.\r\n writebacks: ['M12 3v10', 'M8 9l4 4 4-4', 'M4 14v4a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-4'],\r\n // Lo stesso contenitore, con un lucchetto al posto della freccia.\r\n atomicWritebacks: [\r\n 'M10 7V5.5a2 2 0 0 1 4 0V7',\r\n 'M8.5 7h7v5h-7z',\r\n 'M4 15v3a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-3',\r\n ],\r\n};\r\n\r\n/**\r\n * I tracciati di una collezione. Il ripiego vuoto non e' pigrizia: una collezione aggiunta al\r\n * modello e non ancora disegnata deve produrre una pastiglia vuota, non un errore in console\r\n * ne' un `undefined` dentro `[attr.d]`.\r\n */\r\nexport function dpeNodeIconPaths(collection: DpeNodeCollectionName): readonly string[] {\r\n return DPE_NODE_ICON_PATHS[collection] ?? [];\r\n}\r\n","/**\r\n * Geometria della viewport: funzioni pure, nessun DOM.\r\n *\r\n * Serve a una sola domanda, che si pone quando si naviga da una diagnostica al nodo: **il nodo\r\n * e' interamente in vista?** Se lo e', non si sposta niente — una panoramica non richiesta\r\n * disorienta quanto la sua assenza. Se non lo e', si centra la **vista**, non il nodo: le\r\n * coordinate del documento non si toccano.\r\n *\r\n * Sta qui e non nel componente perche' e' esattamente il tipo di logica che si sbaglia in\r\n * silenzio (un `>` al posto di un `>=`, un margine dimenticato) e che si verifica senza browser.\r\n */\r\n\r\n/** Rettangolo in coordinate di schermo, la forma che `getBoundingClientRect` restituisce. */\r\nexport interface DpeRect {\r\n left: number;\r\n top: number;\r\n right: number;\r\n bottom: number;\r\n}\r\n\r\n/**\r\n * Vero quando `target` sta **interamente** dentro `view`, lasciando `margin` pixel di respiro su\r\n * ogni lato. Il margine non e' un vezzo: un nodo tangente al bordo e' tecnicamente visibile e\r\n * praticamente illeggibile, e senza respiro il criterio \"e' visibile\" scatterebbe anche quando\r\n * dell'ombra e del connettore di uscita non si vede nulla.\r\n */\r\nexport function dpeIsRectFullyVisible(target: DpeRect, view: DpeRect, margin = 0): boolean {\r\n return (\r\n target.left >= view.left + margin &&\r\n target.top >= view.top + margin &&\r\n target.right <= view.right - margin &&\r\n target.bottom <= view.bottom - margin\r\n );\r\n}\r\n","/**\r\n * L'icona di una categoria di nodo.\r\n *\r\n * Rende markup SVG **statico** e lega soltanto `[attr.d]` dei tracciati: nessun `innerHTML`,\r\n * quindi nessun bypass del sanitizer e nessuna dipendenza per le icone. `d` non e' un attributo\r\n * sensibile — e' l'unico punto in cui il dato entra nel markup, ed e' un dato nostro, non del\r\n * documento.\r\n *\r\n * `aria-hidden`: l'icona **non e' informazione**, l'etichetta accanto lo e' gia'. Annunciarla\r\n * farebbe leggere due volte la stessa cosa a chi usa uno screen reader.\r\n *\r\n * La stessa icona compare in tre posti — palette, card del canvas, intestazione dell'ispettore —\r\n * perche' cio' che si trascina deve assomigliare a cio' che compare.\r\n */\r\n\r\nimport { ChangeDetectionStrategy, Component, computed, input } from '@angular/core';\r\n\r\nimport { dpeNodeIconPaths } from '../../core/dpe-node-icons';\r\nimport { DpeNodeCollectionName } from '../../model/dpe-definition.model';\r\n\r\n@Component({\r\n selector: 'dpe-node-icon',\r\n standalone: true,\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n template: `\r\n <svg\r\n viewBox=\"0 0 24 24\"\r\n width=\"16\"\r\n height=\"16\"\r\n fill=\"none\"\r\n stroke=\"currentColor\"\r\n stroke-width=\"2\"\r\n stroke-linecap=\"round\"\r\n stroke-linejoin=\"round\"\r\n aria-hidden=\"true\"\r\n >\r\n <!-- track $index: i tracciati sono una lista ricalcolata, e due uguali sono legittimi. -->\r\n @for (path of paths(); track $index) {\r\n <path [attr.d]=\"path\" />\r\n }\r\n </svg>\r\n `,\r\n})\r\nexport class DpeNodeIconComponent {\r\n readonly collection = input.required<DpeNodeCollectionName>();\r\n\r\n protected readonly paths = computed(() => dpeNodeIconPaths(this.collection()));\r\n}\r\n","/**\r\n * View model del canvas.\r\n *\r\n * Gli **id** non si costruiscono qui: vengono da `core/dpe-graph.ts`, che e' l'unico posto che\r\n * li conosce. Qui stanno solo le misure, e stanno qui perche' servono a due consumatori che\r\n * devono concordare: il CSS che disegna la card e dagre che calcola il layout. Se divergono,\r\n * il layout automatico lascia i nodi sovrapposti.\r\n */\r\n\r\nimport { DiagnosticSeverity } from '../../model/dpe-diagnostics.model';\r\nimport { DpeNodeCollectionName } from '../../model/dpe-definition.model';\r\n\r\n/** Altezza nominale della card: non cambia con il numero di porte, che stanno in fila sopra. */\r\nexport const DPE_NODE_HEIGHT = 104;\r\n\r\n/**\r\n * Larghezza per numero di porte di ingresso. Le porte stanno in fila sul bordo **superiore**,\r\n * quindi e' la larghezza a dipendere dal loro numero.\r\n *\r\n * E' una tabella a scaglioni e non una formula perche' la larghezza si applica con una\r\n * **classe**: @foblex/flow riscrive l'attributo `style` del nodo a ogni riposizionamento e\r\n * qualunque `[style.width]` di Angular verrebbe cancellato al primo spostamento. I valori qui\r\n * e quelli di `.dpe-node--ports-N` nel CSS del componente devono restare uguali.\r\n */\r\nconst PORT_WIDTHS = [230, 230, 260, 320, 380];\r\n\r\nconst MAX_PORT_BUCKET = PORT_WIDTHS.length - 1;\r\n\r\nfunction portBucket(portCount: number): number {\r\n return Math.min(Math.max(portCount, 0), MAX_PORT_BUCKET);\r\n}\r\n\r\nexport function dpeNodeWidth(portCount: number): number {\r\n return PORT_WIDTHS[portBucket(portCount)];\r\n}\r\n\r\nexport function dpeNodeWidthClass(portCount: number): string {\r\n return `dpe-node--ports-${portBucket(portCount)}`;\r\n}\r\n\r\nexport interface DpeCanvasPort {\r\n id: string;\r\n slot: string;\r\n index?: number;\r\n /** Etichetta della porta: si mostra solo dove le porte sono piu' di una. */\r\n label: string;\r\n /** Nome collegato, assente se la porta e' libera. */\r\n sourceName?: string;\r\n /** Il nome collegato non corrisponde a nessun nodo: si dice sulla porta. */\r\n isBroken: boolean;\r\n}\r\n\r\nexport interface DpeCanvasNode {\r\n /** Nome nel documento: l'identita', anche per la selezione. */\r\n name: string;\r\n /** Id per il canvas, costruito da `dpeNodeId`. */\r\n id: string;\r\n collection: DpeNodeCollectionName;\r\n categoryLabel: string;\r\n label: string;\r\n subtitle: string | null;\r\n position: { x: number; y: number };\r\n ports: DpeCanvasPort[];\r\n showsPortLabels: boolean;\r\n widthClass: string;\r\n /**\r\n * Classe della famiglia cromatica (`dpe-family--*`): la calcola `core/dpe-node-icons.ts` dai\r\n * flag del catalogo. Sta nel view model e non nel template perche' e' la stessa che usa la\r\n * palette, e due modi di derivarla sarebbero due colori diversi per lo stesso tipo di nodo.\r\n */\r\n familyClass: string;\r\n outputPortId: string;\r\n /** I sink non producono uno schema: la porta di uscita non si disegna... */\r\n hasOutput: boolean;\r\n /** Gravita' peggiore fra le diagnostiche del nodo. */\r\n severity: DiagnosticSeverity | null;\r\n diagnosticCount: number;\r\n /**\r\n * Nodo **valido ma non compilabile** (**I10**): stato distinto da \"non valido\", con un\r\n * colore proprio. Sono due situazioni diverse e l'utente deve capire subito quale guarda.\r\n */\r\n isNotCompilable: boolean;\r\n /**\r\n * Numero di colonne dello schema di uscita. `null` significa **nessuna pastiglia**, e ci sono\r\n * due modi di arrivarci:\r\n *\r\n * - il backend non ha dichiarato di aver derivato lo schema (`hasSchema`, ADR 0041): un «0 col.»\r\n * sarebbe un'affermazione che nessuno ha fatto;\r\n * - il nodo e' un **pozzo**: il conteggio risponde a «cosa esce di qui», e da un pozzo non esce\r\n * niente. Lo zero e' vero — e' la condizione normale di ogni writeback — ed e' la risposta a\r\n * una domanda che non si pone.\r\n *\r\n * Sui nodi **non** pozzo lo zero si mostra: li' significa `DPE_SCHEMA_EMPTY`, che e' un errore.\r\n */\r\n columnCount: number | null;\r\n}\r\n\r\nexport interface DpeCanvasEdge {\r\n id: string;\r\n sourceId: string;\r\n targetId: string;\r\n slot: string;\r\n}\r\n","/**\r\n * Il canvas del DAG.\r\n *\r\n * @foblex/flow in *classic mode*: la libreria disegna e riconosce i gesti, **lo stato resta\r\n * nostro**. Nodi e archi sono signal derivati da `DpeDocumentStore` attraverso\r\n * `core/dpe-graph.ts`, e ogni gesto e' una mutazione del documento.\r\n *\r\n * Cosa il canvas **non** fa: non decide se un collegamento sia lecito. Un ciclo (**I2**) o un\r\n * riferimento a valle (**I3**) li rifiuta il backend, e la diagnostica arriva subito perche' la\r\n * validazione parte dopo la mutazione. Disegnare solo cio' che il backend accetterebbe\r\n * richiederebbe di riprodurre qui la sua semantica: e' esattamente cio' che non facciamo.\r\n */\r\n\r\nimport {\r\n ChangeDetectionStrategy,\r\n Component,\r\n ElementRef,\r\n computed,\r\n effect,\r\n inject,\r\n input,\r\n output,\r\n signal,\r\n viewChild,\r\n} from '@angular/core';\r\nimport {\r\n EFConnectableSide,\r\n EFMarkerType,\r\n FCanvasComponent,\r\n FFlowComponent,\r\n FFlowModule,\r\n provideFFlow,\r\n withA11y,\r\n} from '@foblex/flow';\r\n\r\nimport { DpeDocumentStore } from '../../core/dpe-document.store';\r\nimport {\r\n dpeAllNodes,\r\n dpeInputPorts,\r\n dpeNodeId,\r\n dpeOutputPortId,\r\n dpeParseInputPortId,\r\n dpeParseNodeId,\r\n dpeParseOutputPortId,\r\n} from '../../core/dpe-graph';\r\nimport { dpeNodeCategory } from '../../core/dpe-node-catalog';\r\nimport { dpeIsNodeCollectionName } from '../../core/dpe-node-collection-guard';\r\nimport { dpeNodeIconFamilyClass } from '../../core/dpe-node-icons';\r\nimport { dpeIsRectFullyVisible } from '../../core/dpe-viewport';\r\nimport { dpeNodeSubtitle } from '../../core/dpe-node-factory';\r\nimport { DpeNodeCollectionName } from '../../model/dpe-definition.model';\r\nimport { DpeNodeIconComponent } from '../shared/dpe-node-icon.component';\r\nimport {\r\n DPE_NODE_HEIGHT,\r\n DpeCanvasEdge,\r\n DpeCanvasNode,\r\n DpeCanvasPort,\r\n dpeNodeWidth,\r\n dpeNodeWidthClass,\r\n} from './dpe-canvas.model';\r\n\r\n/** Etichette delle porte di ingresso, per slot. Presentazione, non semantica. */\r\nconst PORT_LABELS: Readonly<Record<string, string>> = {\r\n source: 'Ingresso',\r\n primary: 'Primaria',\r\n secondary: 'Secondaria',\r\n sources: 'Sorgente',\r\n};\r\n\r\n/**\r\n * Respiro attorno al grafo inquadrato. Era 100 per lato: su un'area alta 590 px erano 200 px di\r\n * vuoto, cioe' un terzo dello spazio, e bastavano a spingere l'inquadratura sotto la soglia di\r\n * leggibilita' — quindi a farla saltare del tutto.\r\n *\r\n * Il valore verticale e' un filo piu' generoso di quello orizzontale, e non per estetica: la card\r\n * disegna piu' del rettangolo con cui e' misurata (le porte sporgono sopra, il connettore di\r\n * uscita sotto), e con un respiro simmetrico l'ultima card restava tangente al bordo inferiore —\r\n * misurato: un pixel fuori.\r\n */\r\nconst FIT_PADDING = { x: 44, y: 56 };\r\n\r\n/**\r\n * Scala minima con cui il canvas si apre. Sotto questa soglia le etichette della card — nome,\r\n * categoria, conteggio delle colonne — sono disegnate in un corpo che non si legge, e si apre al\r\n * 100% centrato lasciando all'utente lo zoom (rotella, `+`/`-`, minimappa, «Inquadra»).\r\n *\r\n * Il valore non e' arbitrario: il titolo della card e' 13px, quindi a 0,75 resta un corpo effettivo\r\n * di ~10px, il minimo che si legge. Era stato abbassato a 0.35 per soddisfare il criterio «nessun\r\n * nodo fuori dal riquadro all'apertura», criterio poi **ritirato** dal committente\r\n * (`docs/frontend.md` §3-ter): su un grafo grande si soddisfa solo rimpicciolendo fino\r\n * all'illeggibile — misurato sul seed della demo, scala 0,516 e titolo effettivo 6,7px. La\r\n * leggibilita' viene prima della vista d'insieme; la vista d'insieme resta a un clic, tramite\r\n * «Inquadra», che in questo ramo si mostra apposta.\r\n */\r\nconst MIN_INITIAL_SCALE = 0.75;\r\n\r\n/**\r\n * Respiro richiesto attorno al nodo perche' si consideri \"in vista\". Un nodo tangente al bordo\r\n * e' visibile e illeggibile: la panoramica scatta anche in quel caso.\r\n */\r\nconst REVEAL_MARGIN = 24;\r\n\r\n@Component({\r\n selector: 'dpe-canvas',\r\n standalone: true,\r\n imports: [FFlowModule, DpeNodeIconComponent],\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n templateUrl: './dpe-canvas.component.html',\r\n /**\r\n * Il layer da tastiera di @foblex/flow e' **opzionale**: senza `withA11y()` la libreria non\r\n * ascolta nessun tasto e `(fDeleteSelected)` — l'unico modo in cui annuncia «l'utente vuole\r\n * cancellare» — non viene mai emesso. Nessun errore: semplicemente `Canc` non fa niente.\r\n * Attiva anche frecce, `Ctrl+A`, `Escape`, zoom da tastiera, cioe' la meta' della\r\n * navigazione senza mouse che un editor a canvas deve avere.\r\n *\r\n * Provider **del componente**: montare il canvas non installa ascoltatori globali nell'app\r\n * ospite.\r\n */\r\n providers: [provideFFlow(withA11y())],\r\n})\r\nexport class DpeCanvasComponent {\r\n private readonly store = inject(DpeDocumentStore);\r\n /** Serve per misurare l'area disponibile prima di decidere la scala d'apertura. */\r\n private readonly host = inject<ElementRef<HTMLElement>>(ElementRef);\r\n\r\n /** Marker \"all states\": senza, l'arco selezionato perde la punta. */\r\n readonly markerEnd = EFMarkerType.END_ALL_STATES;\r\n\r\n /**\r\n * Ingressi in alto, uscita in basso, **dichiarati**. Lasciando calcolare il lato, un arco\r\n * che risale (un nodo posizionato sopra la sua sorgente) esce di fianco e il grafo diventa\r\n * illeggibile.\r\n */\r\n readonly sides = EFConnectableSide;\r\n\r\n readonly isEditable = input(true);\r\n\r\n readonly nodeOpened = output<string>();\r\n readonly nodeDropped = output<{ collection: DpeNodeCollectionName; x: number; y: number }>();\r\n\r\n private readonly canvas = viewChild.required(FCanvasComponent);\r\n /**\r\n * Serve solo a **riallineare** la selezione della libreria a quella dello store: e' l'unico\r\n * punto che scrive nello stato di selezione di @foblex/flow.\r\n */\r\n private readonly flow = viewChild(FFlowComponent);\r\n private hasFramedOnce = false;\r\n\r\n /**\r\n * Visibilita' del comando «Inquadra». Non e' piu' «l'utente ha spostato la vista»: lo diventa\r\n * anche all'apertura quando l'editor rinuncia a inquadrare per non scendere sotto\r\n * `MIN_INITIAL_SCALE`. In quel caso la vista d'insieme non e' stata data, quindi il comando che\r\n * la offre deve essere raggiungibile — nasconderlo significherebbe toglierla e basta.\r\n */\r\n private readonly _isFitCommandVisible = signal(false);\r\n readonly isFitCommandVisible = this._isFitCommandVisible.asReadonly();\r\n\r\n readonly nodes = computed<DpeCanvasNode[]>(() => {\r\n const definition = this.store.definition();\r\n const positions = this.store.positions();\r\n const diagnostics = this.store.diagnosticsByNode();\r\n const schemas = this.store.schemaByNode();\r\n const known = new Set(this.store.graph().nodes.map((node) => node.name));\r\n\r\n // Si cammina la definizione, non il grafo: servono i nodi veri (etichetta, sintesi) e le\r\n // porte, che si leggono dagli slot del nodo.\r\n return dpeAllNodes(definition).map((located) => {\r\n const category = dpeNodeCategory(located.collection);\r\n const ports: DpeCanvasPort[] = dpeInputPorts(located).map((port) => ({\r\n id: port.id,\r\n slot: port.slot,\r\n index: port.index,\r\n label:\r\n port.index === undefined\r\n ? (PORT_LABELS[port.slot] ?? port.slot)\r\n : `${PORT_LABELS[port.slot] ?? port.slot} ${port.index + 1}`,\r\n sourceName: port.sourceName,\r\n // Un nome che non corrisponde a nessun nodo non e' disegnabile come arco — non\r\n // esiste il connettore da cui partirebbe — quindi si dice **sulla porta**. Nasconderlo\r\n // sarebbe l'unico modo di non vedere un riferimento rotto.\r\n isBroken: port.sourceName !== undefined && !known.has(port.sourceName),\r\n }));\r\n const name = located.node.name;\r\n const nodeDiagnostics = diagnostics.get(name);\r\n const schema = schemas.get(name);\r\n\r\n return {\r\n name,\r\n id: dpeNodeId(name),\r\n collection: located.collection,\r\n categoryLabel: category.label,\r\n label: located.node.label || name,\r\n subtitle: dpeNodeSubtitle(located.collection, located.node),\r\n position: positions.get(name) ?? { x: 0, y: 0 },\r\n ports,\r\n showsPortLabels: ports.length > 1,\r\n widthClass: dpeNodeWidthClass(ports.length),\r\n familyClass: dpeNodeIconFamilyClass(located.collection),\r\n outputPortId: dpeOutputPortId(name),\r\n hasOutput: !category.isSink,\r\n severity: nodeDiagnostics?.severity ?? null,\r\n diagnosticCount: nodeDiagnostics?.diagnostics.length ?? 0,\r\n // Due fonti concordi: la diagnostica del backend (autorevole) e lo schema, che porta\r\n // `isCompilable`. La categoria «non compilabile in v1» del catalogo e' solo un\r\n // suggerimento della palette e qui non basterebbe.\r\n isNotCompilable:\r\n (nodeDiagnostics?.isNotCompilable ?? false) || schema?.isCompilable === false,\r\n // Due condizioni perche' il numero compaia, e rispondono a due domande diverse.\r\n //\r\n // 1. Il backend ha davvero derivato lo schema (`hasSchema`, ADR 0041): a schema ignoto\r\n // `columns` e' vuota perche' la risposta non c'e', e «0 col.» affermerebbe un fatto che\r\n // nessuno ha affermato.\r\n // 2. Il nodo **non e' un pozzo**. Il conteggio risponde a «cosa esce di qui», e da un\r\n // pozzo non esce niente: lo zero e' la risposta a una domanda che non si pone. Ogni\r\n // writeback sano porta `hasSchema: true` con zero colonne — condizione **normale**\r\n // (ADR 0041 addendum 1), non un difetto — quindi il numero comparirebbe su ogni pozzo\r\n // di ogni definizione, sempre. Sui nodi non pozzo lo zero **resta**: li' e'\r\n // `DPE_SCHEMA_EMPTY`, cioe' un errore, ed e' informativo.\r\n //\r\n // La categoria e' la stessa che decide `hasOutput` qui sopra: nessun dato nuovo, nessuna\r\n // deduzione. `null` = nessuna pastiglia.\r\n // `schema.columns.length` senza ripiego: la collezione e' obbligatoria nel contratto, e un\r\n // `?? 0` avrebbe confuso «zero colonne» con «la collezione non e' arrivata» proprio nella\r\n // pastiglia che quello zero lo mostra.\r\n columnCount: !category.isSink && schema?.hasSchema === true ? schema.columns.length : null,\r\n };\r\n });\r\n });\r\n\r\n /**\r\n * Solo gli archi con **entrambi** i capi risolti diventano `<f-connection>`: un capo che non\r\n * esiste non ha un connettore, e una connessione verso un id inesistente si attacca a un\r\n * punto arbitrario del canvas. Il riferimento rotto si vede sulla porta del nodo.\r\n */\r\n readonly edges = computed<DpeCanvasEdge[]>(() =>\r\n this.store\r\n .graph()\r\n .edges.filter((edge) => edge.isResolved)\r\n .map((edge) => ({\r\n id: edge.id,\r\n sourceId: edge.fromPortId,\r\n targetId: edge.toPortId,\r\n slot: edge.slot,\r\n })),\r\n );\r\n\r\n constructor() {\r\n /*\r\n * Riallineamento della selezione: **una sola scrittura**, e in questa direzione.\r\n *\r\n * @foblex/flow tiene una sua selezione (classe `f-selected`, elenco interno usato da drag\r\n * multiplo e `Canc`) e la aggiorna da se' sui gesti. Se la si lascia autorevole si finisce\r\n * con due sistemi che non si parlano: il nodo raggiunto da una diagnostica porta la nostra\r\n * classe, quello cliccato prima porta `f-selected`, e l'utente vede due nodi selezionati\r\n * senza sapere su quale sta agendo. La cura non e' azzerare l'altro sistema dove serve — e'\r\n * derivarlo: il gesto entra da `fSelectionChange` e finisce nello store, lo store torna qui e\r\n * scrive nella libreria. La classe `dpe-node--selected` deriva dallo stesso stato.\r\n *\r\n * `isSelectedChanged: false` perche' questa scrittura **non** e' un gesto dell'utente e non\r\n * deve far emettere un cambio di selezione: sarebbe il ciclo.\r\n */\r\n effect(() => {\r\n const selected = this.store.selectedNodeNames();\r\n const flow = this.flow();\r\n if (!flow) {\r\n return;\r\n }\r\n if (selected.length === 0) {\r\n // `clearSelection` agisce subito; `select` no (vedi sotto), quindi il caso vuoto passa da qui.\r\n flow.clearSelection();\r\n return;\r\n }\r\n flow.select(selected.map((name) => dpeNodeId(name)), [], false);\r\n // `select()` della libreria non applica: si iscrive al prossimo cambio delle connessioni e\r\n // agisce **la' dentro**. Senza una notifica, la selezione programmatica resta in attesa\r\n // indefinitamente e nel DOM non compare niente — nessun errore, solo un `f-selected` che\r\n // non arriva. `redraw()` e' cio' che emette quel cambio.\r\n flow.redraw();\r\n });\r\n }\r\n\r\n // -------------------------------------------------------------------------\r\n // Navigazione\r\n // -------------------------------------------------------------------------\r\n\r\n /**\r\n * Porta il nodo in vista **muovendo la vista**, non il nodo: le coordinate del documento non si\r\n * toccano (una navigazione non e' una modifica). Se il nodo e' gia' interamente visibile non fa\r\n * nulla, perche' una panoramica non richiesta disorienta quanto la sua assenza.\r\n */\r\n revealNode(name: string): void {\r\n const id = dpeNodeId(name);\r\n const element = this.findNodeElement(id);\r\n if (!element) {\r\n return;\r\n }\r\n const view = this.host.nativeElement.getBoundingClientRect();\r\n if (dpeIsRectFullyVisible(element.getBoundingClientRect(), view, REVEAL_MARGIN)) {\r\n return;\r\n }\r\n // La panoramica e' un movimento della vista fatto da noi, ma resta un movimento: lasciando\r\n // emettere `fCanvasChange` compare «Inquadra», che e' il modo di tornare all'insieme.\r\n this.canvas().centerGroupOrNode(id, true);\r\n }\r\n\r\n /**\r\n * Il nodo nel DOM. Si scorre l'elenco invece di comporre un selettore con il valore\r\n * dell'attributo: il nome del nodo arriva dal documento e puo' contenere qualunque carattere,\r\n * e un selettore costruito per concatenazione su un dato del documento e' un errore in attesa.\r\n */\r\n private findNodeElement(id: string): HTMLElement | undefined {\r\n const elements = this.host.nativeElement.querySelectorAll<HTMLElement>('[data-f-node-id]');\r\n for (const element of Array.from(elements)) {\r\n if (element.getAttribute('data-f-node-id') === id) {\r\n return element;\r\n }\r\n }\r\n return undefined;\r\n }\r\n\r\n /** Misure per il layout automatico: le stesse che il CSS applica con le classi. */\r\n layoutInput(): { name: string; width: number; height: number }[] {\r\n return this.nodes().map((node) => ({\r\n name: node.name,\r\n width: dpeNodeWidth(node.ports.length),\r\n height: DPE_NODE_HEIGHT,\r\n }));\r\n }\r\n\r\n // -------------------------------------------------------------------------\r\n // Viewport\r\n // -------------------------------------------------------------------------\r\n\r\n /**\r\n * `fitToScreen` va chiamato **dopo** il render dei nodi: prima, la libreria non conosce\r\n * ancora le loro dimensioni e inquadra la vista sbagliata. Solo la prima volta: rifarlo\r\n * combatterebbe con l'utente che ha spostato la vista.\r\n */\r\n onNodesRendered(): void {\r\n if (this.hasFramedOnce || this.nodes().length < 2) {\r\n return;\r\n }\r\n /*\r\n * Si inquadra **dopo** che tutti i nodi hanno una posizione. Un documento che non porta\r\n * coordinate (ADR 0013) le riceve da dagre subito dopo il primo render: inquadrare prima\r\n * significava inquadrare un mucchio di card tutte in (0,0), e il risultato era il grafo\r\n * tagliato in alto a sinistra all'apertura — con `hasFramedOnce` gia' consumato, quindi senza\r\n * una seconda occasione. Qui non si consuma niente: il layout scrive le posizioni, i nodi si\r\n * ridisegnano e questo gestore torna con le misure vere.\r\n */\r\n const positions = this.store.positions();\r\n if (this.nodes().some((node) => !positions.has(node.name))) {\r\n return;\r\n }\r\n this.hasFramedOnce = true;\r\n const canvas = this.canvas();\r\n // La scala si **stima prima**: chiedere `getScale()` dopo `fitToScreen` restituisce la scala\r\n // di configurazione e non quella applicata, quindi il controllo non scattava mai e le card\r\n // restavano illeggibili. La stima usa gli stessi numeri che il CSS applica alle card.\r\n if (this.wouldFitBelowMinimumScale()) {\r\n // 100% centrato sui nodi: si perde la vista d'insieme e si guadagna un'etichetta\r\n // leggibile. `emitCanvasChange: false` perche' l'inquadratura iniziale non e' un gesto\r\n // dell'utente; il comando «Inquadra» si mostra qui in modo esplicito, perche' e' l'unico\r\n // modo di ottenere la panoramica a cui si e' appena rinunciato.\r\n canvas.resetScaleAndCenter(false, false);\r\n this._isFitCommandVisible.set(true);\r\n return;\r\n }\r\n canvas.fitToScreen(FIT_PADDING, false, false);\r\n }\r\n\r\n /** Vero quando inquadrare tutto il grafo richiederebbe una scala sotto la soglia leggibile. */\r\n private wouldFitBelowMinimumScale(): boolean {\r\n const nodes = this.nodes();\r\n if (nodes.length === 0) {\r\n return false;\r\n }\r\n let left = Number.POSITIVE_INFINITY;\r\n let top = Number.POSITIVE_INFINITY;\r\n let right = Number.NEGATIVE_INFINITY;\r\n let bottom = Number.NEGATIVE_INFINITY;\r\n for (const node of nodes) {\r\n const width = dpeNodeWidth(node.ports.length);\r\n left = Math.min(left, node.position.x);\r\n top = Math.min(top, node.position.y);\r\n right = Math.max(right, node.position.x + width);\r\n bottom = Math.max(bottom, node.position.y + DPE_NODE_HEIGHT);\r\n }\r\n const host = this.host.nativeElement;\r\n const availableWidth = host.clientWidth - FIT_PADDING.x * 2;\r\n const availableHeight = host.clientHeight - FIT_PADDING.y * 2;\r\n if (availableWidth <= 0 || availableHeight <= 0) {\r\n return false;\r\n }\r\n const scale = Math.min(\r\n availableWidth / Math.max(right - left, 1),\r\n availableHeight / Math.max(bottom - top, 1),\r\n );\r\n return scale < MIN_INITIAL_SCALE;\r\n }\r\n\r\n onCanvasChange(): void {\r\n this._isFitCommandVisible.set(true);\r\n }\r\n\r\n resetViewport(): void {\r\n this.canvas().fitToScreen(FIT_PADDING, true, false);\r\n // Il grafo e' inquadrato: il comando non ha piu' niente da offrire finche' la vista non si muove.\r\n this._isFitCommandVisible.set(false);\r\n }\r\n\r\n // -------------------------------------------------------------------------\r\n // Gesti\r\n // -------------------------------------------------------------------------\r\n\r\n /**\r\n * Creazione di un arco: si scrive il **riferimento** sulla porta di destinazione. La\r\n * direzione del gesto e' irrilevante — cio' che conta e' quale slot riceve il nome, ed e'\r\n * per questo che le porte di ingresso sono una per slot.\r\n */\r\n onCreateConnection(event: { sourceId: string; targetId?: string }): void {\r\n if (!this.isEditable() || !event.targetId) {\r\n return;\r\n }\r\n const source = dpeParseOutputPortId(event.sourceId);\r\n const target = dpeParseInputPortId(event.targetId);\r\n if (!source || !target || source === target.nodeName) {\r\n // Un nodo che si collega a se stesso e' un ciclo di lunghezza uno: il backend lo\r\n // rifiuterebbe con `DPE_GRAPH_CYCLE`, e disegnarlo non aggiunge informazione.\r\n return;\r\n }\r\n this.store.connect(target.nodeName, target.slot, source, target.index);\r\n }\r\n\r\n /** Riassegnazione di un arco gia' disegnato, da un capo o dall'altro. */\r\n onReassignConnection(event: {\r\n endpoint: string;\r\n previousSourceId: string;\r\n nextSourceId?: string;\r\n previousTargetId: string;\r\n nextTargetId?: string;\r\n }): void {\r\n if (!this.isEditable()) {\r\n return;\r\n }\r\n const previousTarget = dpeParseInputPortId(event.previousTargetId);\r\n const previousSource = dpeParseOutputPortId(event.previousSourceId);\r\n if (!previousTarget || !previousSource) {\r\n return;\r\n }\r\n if (event.endpoint === 'target') {\r\n const nextTarget = event.nextTargetId ? dpeParseInputPortId(event.nextTargetId) : undefined;\r\n this.store.disconnect(previousTarget.nodeName, previousTarget.slot, previousTarget.index);\r\n if (nextTarget) {\r\n this.store.connect(nextTarget.nodeName, nextTarget.slot, previousSource, nextTarget.index);\r\n }\r\n return;\r\n }\r\n const nextSource = event.nextSourceId ? dpeParseOutputPortId(event.nextSourceId) : undefined;\r\n if (nextSource) {\r\n this.store.connect(previousTarget.nodeName, previousTarget.slot, nextSource, previousTarget.index);\r\n }\r\n }\r\n\r\n /**\r\n * Spostamento: un solo passo di cronologia per l'intero gesto. Le posizioni intermedie non\r\n * sono modifiche che l'utente vuole annullare una per una.\r\n */\r\n onMoveNodes(event: { nodes: Array<{ id: string; position: { x: number; y: number } }> }): void {\r\n event.nodes.forEach((moved, index) => {\r\n const name = dpeParseNodeId(moved.id);\r\n if (name) {\r\n this.store.moveNode(name, moved.position, { silent: index > 0 });\r\n }\r\n });\r\n }\r\n\r\n /**\r\n * Il gesto di selezione della libreria entra **nello store** e non altrove: e' l'unico verso in\r\n * cui la selezione di @foblex/flow conta, come rilevazione del gesto. Cio' che si vede lo decide\r\n * lo store, attraverso l'effect del costruttore.\r\n */\r\n onSelectionChange(event: { nodeIds: string[] }): void {\r\n const names: string[] = [];\r\n for (const id of event.nodeIds) {\r\n const name = dpeParseNodeId(id);\r\n if (name) {\r\n names.push(name);\r\n }\r\n }\r\n this.store.selectMany(names);\r\n }\r\n\r\n onDeleteSelected(event: { nodeIds: string[] }): void {\r\n if (!this.isEditable()) {\r\n return;\r\n }\r\n for (const id of event.nodeIds) {\r\n const name = dpeParseNodeId(id);\r\n if (name) {\r\n this.store.removeNode(name);\r\n }\r\n }\r\n }\r\n\r\n /**\r\n * Rilascio di una voce trascinata dalla palette. `externalItemRect` e' gia' nel sistema di\r\n * coordinate del canvas: convertirlo a mano dal viewport sarebbe rifare la trasformazione\r\n * di zoom e panoramica della libreria.\r\n *\r\n * `data` e' `unknown` per @foblex/flow, e qui il dato si **controlla** invece di convertirlo con\r\n * un `as`: un rilascio che non porta una collezione di nodi si ignora, come si ignorava un dato\r\n * vuoto. E' il confine che permette a `dpeNodeCategory` di lanciare fuori dall'unione.\r\n */\r\n onCreateNode(event: { data: unknown; externalItemRect: { x: number; y: number } }): void {\r\n if (!this.isEditable()) {\r\n return;\r\n }\r\n const collection = event.data;\r\n if (!dpeIsNodeCollectionName(collection)) {\r\n return;\r\n }\r\n this.nodeDropped.emit({\r\n collection,\r\n x: Math.round(event.externalItemRect.x),\r\n y: Math.round(event.externalItemRect.y),\r\n });\r\n }\r\n\r\n onNodeDoubleClick(name: string): void {\r\n this.nodeOpened.emit(name);\r\n }\r\n\r\n onEditClick(event: Event, name: string): void {\r\n // Senza `stopPropagation` il clic vale anche come selezione dell'area sotto il bottone.\r\n event.stopPropagation();\r\n this.nodeOpened.emit(name);\r\n }\r\n\r\n onRemoveClick(event: Event, name: string): void {\r\n event.stopPropagation();\r\n this.store.removeNode(name);\r\n }\r\n\r\n /** La classe nel DOM **deriva** dallo store: non e' una seconda registrazione della selezione. */\r\n isSelected(node: DpeCanvasNode): boolean {\r\n return this.store.selectedNodeNameSet().has(node.name);\r\n }\r\n}\r\n","<!--\r\n Gerarchia obbligatoria: f-flow > f-canvas > fNode / f-connection. I `@for` stanno\r\n direttamente dentro <f-canvas>: senza wrapper non serve `ngProjectAs`.\r\n-->\r\n<!--\r\n `dpe-flow`: classe nostra sul contenitore della libreria, e serve al **CSS**. Il tema di\r\n @foblex/flow stila la card con `.f-flow .f-node` (due classi): un nostro `.dpe-node` non la\r\n batte, e senza questa classe la card torna larga 120px e col testo centrato. Con `dpe-flow` i\r\n selettori del foglio diventano `dpe-canvas .dpe-flow .dpe-node`, che vince per specificita' e\r\n non per ordine di caricamento.\r\n-->\r\n<f-flow\r\n class=\"dpe-flow\"\r\n fDraggable\r\n (fCreateConnection)=\"onCreateConnection($event)\"\r\n (fReassignConnection)=\"onReassignConnection($event)\"\r\n (fMoveNodes)=\"onMoveNodes($event)\"\r\n (fSelectionChange)=\"onSelectionChange($event)\"\r\n (fDeleteSelected)=\"onDeleteSelected($event)\"\r\n (fCreateNode)=\"onCreateNode($event)\"\r\n (fNodesRendered)=\"onNodesRendered()\"\r\n>\r\n <f-canvas fZoom #canvas (fCanvasChange)=\"onCanvasChange()\">\r\n <f-background>\r\n <f-circle-pattern />\r\n </f-background>\r\n\r\n @for (edge of edges(); track edge.id) {\r\n <!--\r\n La classe porta lo **slot** di destinazione: la primaria e la secondaria di un join\r\n vanno distinte a vista, altrimenti l’unico modo di sapere quale lato e’ quale e’ aprire\r\n l’ispettore. I colori stanno nel CSS globale, non qui: i path SVG sono creati a runtime\r\n e non portano l’attributo di scope.\r\n -->\r\n <f-connection\r\n [fConnectionId]=\"edge.id\"\r\n [fSourceId]=\"edge.sourceId\"\r\n [fTargetId]=\"edge.targetId\"\r\n fBehavior=\"floating\"\r\n fType=\"bezier\"\r\n [class]=\"'dpe-edge dpe-edge--' + edge.slot\"\r\n >\r\n <f-connection-marker-arrow [type]=\"markerEnd\" />\r\n </f-connection>\r\n }\r\n\r\n @for (node of nodes(); track node.id) {\r\n <!--\r\n Nessun `[style.*]` sul nodo: @foblex/flow riscrive l’attributo `style` a ogni\r\n riposizionamento e lo cancellerebbe. La larghezza arriva da `node.widthClass`.\r\n -->\r\n <div\r\n fNode\r\n fDragHandle\r\n [fNodeId]=\"node.id\"\r\n [fNodePosition]=\"node.position\"\r\n [class]=\"\r\n 'dpe-node dpe-node--' + node.collection + ' ' + node.widthClass + ' ' + node.familyClass\r\n \"\r\n [class.dpe-node--selected]=\"isSelected(node)\"\r\n [class.dpe-node--error]=\"node.severity === 'Error'\"\r\n [class.dpe-node--warning]=\"node.severity === 'Warning'\"\r\n [class.dpe-node--not-compilable]=\"node.isNotCompilable\"\r\n [attr.aria-label]=\"node.categoryLabel + ' ' + node.label\"\r\n (dblclick)=\"onNodeDoubleClick(node.name)\"\r\n >\r\n <!--\r\n Le porte di ingresso stanno in fila sul bordo superiore, una per slot. Si renderizzano\r\n **sempre**: un connettore con `display: none` ha geometria 0x0 e l’arco si attacca nel\r\n punto sbagliato. `fConnectorConnectableSide` e’ dichiarato: senza, un arco che risale\r\n entrerebbe di fianco.\r\n -->\r\n @if (node.ports.length > 0) {\r\n <div class=\"dpe-node__ports\" [class.dpe-node__ports--labelled]=\"node.showsPortLabels\">\r\n @for (port of node.ports; track port.id) {\r\n <div class=\"dpe-port\">\r\n <div\r\n fConnector\r\n fConnectorType=\"target\"\r\n [fConnectorId]=\"port.id\"\r\n [fConnectorConnectableSide]=\"sides.TOP\"\r\n fConnectorMultiple=\"false\"\r\n class=\"dpe-connector dpe-connector--in\"\r\n [class.dpe-connector--broken]=\"port.isBroken\"\r\n [title]=\"port.label\"\r\n ></div>\r\n @if (node.showsPortLabels) {\r\n <span class=\"dpe-port__label\" [title]=\"port.label\">{{ port.label }}</span>\r\n }\r\n </div>\r\n }\r\n </div>\r\n }\r\n\r\n <div class=\"dpe-node__head\">\r\n <!--\r\n La stessa pastiglia della palette: cio’ che si trascina deve assomigliare a cio’ che\r\n compare. La classe di famiglia porta i due token di colore.\r\n -->\r\n <span class=\"dpe-node__icon\">\r\n <dpe-node-icon [collection]=\"node.collection\" />\r\n </span>\r\n <div class=\"dpe-node__text\">\r\n <span class=\"dpe-node__title\" [title]=\"node.label\">{{ node.label }}</span>\r\n <span class=\"dpe-node__sub\">\r\n {{ node.categoryLabel }}\r\n @if (node.subtitle) {\r\n <span class=\"dpe-node__dot\">·</span>{{ node.subtitle }}\r\n }\r\n </span>\r\n </div>\r\n <!--\r\n `fDragBlocker` sui bottoni dentro un nodo: senza, il pointerdown diventa un\r\n trascinamento e aprire l’ispettore sposta il nodo di qualche pixel.\r\n -->\r\n <button\r\n type=\"button\"\r\n fDragBlocker\r\n class=\"dpe-node__btn\"\r\n [attr.aria-label]=\"'Apri l’ispettore di ' + node.label\"\r\n title=\"Apri l’ispettore\"\r\n (click)=\"onEditClick($event, node.name)\"\r\n >\r\n ✎\r\n </button>\r\n @if (isEditable() && isSelected(node)) {\r\n <!--\r\n Solo sul nodo selezionato: un comando distruttivo che compare sotto il puntatore\r\n mentre si attraversa il grafo si preme per sbaglio.\r\n -->\r\n <button\r\n type=\"button\"\r\n fDragBlocker\r\n class=\"dpe-node__btn dpe-node__btn--remove\"\r\n [attr.aria-label]=\"'Elimina ' + node.label\"\r\n title=\"Elimina (si annulla con ↶)\"\r\n (click)=\"onRemoveClick($event, node.name)\"\r\n >\r\n ×\r\n </button>\r\n }\r\n </div>\r\n\r\n <div class=\"dpe-node__meta\">\r\n <span class=\"dpe-node__name\" [title]=\"'Nome tecnico: ' + node.name\">{{ node.name }}</span>\r\n @if (node.columnCount !== null) {\r\n <span class=\"dpe-badge\" [title]=\"'Colonne in uscita secondo il backend'\">\r\n {{ node.columnCount }} col.\r\n </span>\r\n }\r\n @if (node.isNotCompilable) {\r\n <!--\r\n Stato distinto dall’errore: la definizione va bene, il compilatore non ci arriva\r\n (I10). Colore proprio, testo che non chiede di correggere niente.\r\n -->\r\n <span class=\"dpe-badge dpe-badge--not-compilable\" title=\"Valido, ma non compilabile: e’ un limite nostro\">\r\n non compilabile\r\n </span>\r\n }\r\n @if (node.diagnosticCount > 0) {\r\n <span\r\n class=\"dpe-badge\"\r\n [class.dpe-badge--error]=\"node.severity === 'Error'\"\r\n [class.dpe-badge--warning]=\"node.severity === 'Warning'\"\r\n [class.dpe-badge--info]=\"node.severity === 'Info'\"\r\n [title]=\"node.diagnosticCount + ' rilievi dal backend'\"\r\n >\r\n {{ node.diagnosticCount }}\r\n </span>\r\n }\r\n @for (port of node.ports; track $index) {\r\n @if (port.isBroken) {\r\n <span class=\"dpe-badge dpe-badge--error\" [title]=\"'Riferimento non risolto: ' + port.sourceName\">\r\n ⚠ {{ port.sourceName }}\r\n </span>\r\n }\r\n }\r\n </div>\r\n\r\n <!--\r\n L’uscita e’ una sola: lo schema prodotto e’ uno. I sink non ne hanno, e la porta non\r\n si disegna affatto — qui non c’e’ niente da nascondere con `display: none`, il\r\n connettore semplicemente non esiste.\r\n -->\r\n @if (node.hasOutput) {\r\n <div class=\"dpe-node__out\">\r\n <div\r\n fConnector\r\n fConnectorType=\"source\"\r\n [fConnectorId]=\"node.outputPortId\"\r\n [fConnectorConnectableSide]=\"sides.BOTTOM\"\r\n class=\"dpe-connector dpe-connector--out\"\r\n title=\"Uscita\"\r\n ></div>\r\n </div>\r\n }\r\n </div>\r\n }\r\n\r\n <f-connection-for-create fBehavior=\"floating\" fType=\"bezier\" class=\"dpe-edge dpe-edge--creating\">\r\n <f-connection-marker-arrow [type]=\"markerEnd\" />\r\n </f-connection-for-create>\r\n\r\n <f-selection-area />\r\n </f-canvas>\r\n\r\n <!--\r\n Fuori da <f-canvas> come la minimappa: dentro, la trasformazione del canvas se lo\r\n porterebbe via insieme ai nodi.\r\n -->\r\n <button\r\n type=\"button\"\r\n fDragBlocker\r\n class=\"dpe-viewport-reset\"\r\n [class.dpe-viewport-reset--hidden]=\"!isFitCommandVisible()\"\r\n title=\"Rimetti il grafo interamente in vista\"\r\n (click)=\"resetViewport()\"\r\n >\r\n ⤢ Inquadra\r\n </button>\r\n\r\n <f-minimap [fMinSize]=\"1200\" class=\"dpe-minimap\" />\r\n</f-flow>\r\n","/**\r\n * Il pannello delle diagnostiche.\r\n *\r\n * Mostra l'**elenco completo** (la validazione e' esaustiva, **I6**): non si accorpa, non si\r\n * tronca, non si riordina per gravita' — l'ordine del backend segue la struttura del documento e\r\n * perderlo rende impossibile capire quale nodo ha quale problema.\r\n *\r\n * La distinzione che questo pannello esiste per rendere: **nodo non valido** (l'utente deve\r\n * correggere) contro **nodo valido ma non ancora compilabile** (`DPE_NODE_NOT_COMPILABLE`,\r\n * **I10**). Sono situazioni diverse e hanno colore, icona ed etichetta diversi; mescolarle\r\n * significherebbe chiedere all'utente di correggere un limite nostro.\r\n *\r\n * Terza categoria a se': i rilievi **del dialetto** (`DPE_DIALECT_*`). Lo stesso documento su un\r\n * altro dialetto sarebbe compilabile, e l'interfaccia deve dirlo, altrimenti si cerca l'errore\r\n * nella definizione.\r\n */\r\n\r\nimport { ChangeDetectionStrategy, Component, computed, inject, output } from '@angular/core';\r\n\r\nimport { DpeDocumentStore } from '../../core/dpe-document.store';\r\nimport {\r\n dpeDiagnosticLocationLabel,\r\n dpeDiagnosticNodeName,\r\n dpeIsDialectDiagnostic,\r\n dpeIsNotCompilable,\r\n} from '../../core/dpe-diagnostics.util';\r\nimport { DPE_DIALECT_LABELS } from '../../core/dpe-node-catalog';\r\nimport { Diagnostic } from '../../model/dpe-diagnostics.model';\r\n\r\ninterface DpeDiagnosticRow {\r\n diagnostic: Diagnostic;\r\n location: string;\r\n /** Nodo a cui navigare, se il percorso permette di ricavarlo. */\r\n nodeName?: string;\r\n field?: string;\r\n isNotCompilable: boolean;\r\n isDialect: boolean;\r\n}\r\n\r\n@Component({\r\n selector: 'dpe-diagnostics-panel',\r\n standalone: true,\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n template: `\r\n <div class=\"dpe-diag__head\">\r\n <strong>Diagnostiche</strong>\r\n <span class=\"dpe-diag__count\">\r\n {{ counts().errors }} errori · {{ counts().warnings }} avvisi · {{ counts().infos }} info\r\n @if (counts().notCompilable > 0) {\r\n · {{ counts().notCompilable }} non compilabili\r\n }\r\n </span>\r\n </div>\r\n\r\n @if (rows().length === 0) {\r\n <p class=\"dpe-diag__empty\">\r\n @if (hasResult()) {\r\n Nessun rilievo dal backend.\r\n } @else {\r\n In attesa della prima validazione…\r\n }\r\n </p>\r\n }\r\n\r\n <!--\r\n track $index: le chiavi composte da codice e percorso si duplicano davvero (lo stesso\r\n codice sullo stesso campo di due righe di una collezione) e Angular lo tratta come errore.\r\n -->\r\n @for (row of rows(); track $index) {\r\n <!-- Una sola sorgente di classi: class statico e [class] si contenderebbero l’attributo. -->\r\n <button\r\n type=\"button\"\r\n [class]=\"'dpe-diag__row dpe-diag__row--' + row.diagnostic.severity\"\r\n [class.dpe-diag__row--not-compilable]=\"row.isNotCompilable\"\r\n [disabled]=\"!row.nodeName\"\r\n (click)=\"navigateTo(row)\"\r\n >\r\n <span class=\"dpe-diag__code\">{{ row.diagnostic.code }}</span>\r\n <span class=\"dpe-diag__text\">\r\n {{ row.diagnostic.message }}\r\n <br />\r\n <span class=\"dpe-diag__where\">{{ row.location }}</span>\r\n </span>\r\n @if (row.isNotCompilable) {\r\n <span\r\n class=\"dpe-diag__tag dpe-diag__tag--not-compilable\"\r\n title=\"Valido, ma il compilatore non lo traduce: non c’e’ nulla da correggere\"\r\n >\r\n limite nostro\r\n </span>\r\n }\r\n @if (row.isDialect) {\r\n <span\r\n class=\"dpe-diag__tag dpe-diag__tag--dialect\"\r\n [title]=\"'Dipende dalle capacita’ del dialetto ' + dialectLabel() + ': su un altro dialetto lo stesso documento potrebbe compilare'\"\r\n >\r\n {{ dialectLabel() }}\r\n </span>\r\n }\r\n </button>\r\n }\r\n `,\r\n})\r\nexport class DpeDiagnosticsPanelComponent {\r\n private readonly store = inject(DpeDocumentStore);\r\n\r\n /** Emesso quando la riga porta a un nodo: chi ascolta apre l'ispettore e inquadra il nodo. */\r\n readonly navigate = output<{ nodeName: string; field?: string }>();\r\n\r\n readonly hasResult = computed(() => this.store.validateResult() !== undefined);\r\n\r\n readonly dialectLabel = computed(() => {\r\n const dialect = this.store.definition().dialect;\r\n return DPE_DIALECT_LABELS[dialect] ?? dialect;\r\n });\r\n\r\n readonly rows = computed<DpeDiagnosticRow[]>(() => {\r\n const definition = this.store.definition();\r\n return this.store.diagnostics().map((diagnostic) => ({\r\n diagnostic,\r\n location: dpeDiagnosticLocationLabel(diagnostic, definition),\r\n nodeName: dpeDiagnosticNodeName(diagnostic, definition),\r\n field: diagnostic.path?.field,\r\n isNotCompilable: dpeIsNotCompilable(diagnostic),\r\n isDialect: dpeIsDialectDiagnostic(diagnostic),\r\n }));\r\n });\r\n\r\n readonly counts = computed(() => {\r\n let errors = 0;\r\n let warnings = 0;\r\n let infos = 0;\r\n let notCompilable = 0;\r\n for (const row of this.rows()) {\r\n if (row.isNotCompilable) {\r\n notCompilable += 1;\r\n }\r\n switch (row.diagnostic.severity) {\r\n case 'Error':\r\n errors += 1;\r\n break;\r\n case 'Warning':\r\n warnings += 1;\r\n break;\r\n default:\r\n infos += 1;\r\n break;\r\n }\r\n }\r\n return { errors, warnings, infos, notCompilable };\r\n });\r\n\r\n /**\r\n * Emette **soltanto** l'intenzione: chi la riceve seleziona e inquadra. Selezionare anche da qui\r\n * significherebbe due scritture della selezione per un solo clic — la strada per due nodi\r\n * selezionati insieme — e un pannello che pilota il canvas invece di chiederglielo.\r\n */\r\n protected navigateTo(row: DpeDiagnosticRow): void {\r\n if (!row.nodeName) {\r\n return;\r\n }\r\n this.navigate.emit({ nodeName: row.nodeName, field: row.field });\r\n }\r\n}\r\n","/**\r\n * I **fatti dal backend** su un nodo: schema in uscita e rilievi che lo riguardano.\r\n *\r\n * Sono in un componente a se' perche' nella dialog di modifica (ADR 0026) non stanno dove stanno\r\n * i campi: i campi prendono la colonna larga, questi la colonna stretta di destra. Metterli **di\r\n * fianco** e non sopra il form evita in anticipo il muro di avvisi che spinge il primo campo\r\n * fuori dallo schermo — con i rilievi in colonna il muro non si forma, quindi si mostrano\r\n * **tutti** (**I6**: l'elenco della validazione e' esaustivo, e troncarlo sarebbe mentire).\r\n *\r\n * In testa sta lo stato **valido ma non compilabile** (**I10**), perche' e' il fatto che spiega gli\r\n * altri due: senza di esso una colonna che elenca i rilievi tace proprio sul motivo per cui da\r\n * questo nodo non uscira' nessun SQL. «Non compilabile» e «non valido» sono due significati\r\n * distinti, e l'editor li tiene distinti per decisione (ADR 0011).\r\n *\r\n * Non calcola niente. Lo schema arriva da `validate` (**I4**: la propagazione e' del backend) e i\r\n * rilievi arrivano gia' raggruppati per nodo: qui si legge e si rende.\r\n *\r\n * **Un pannello di fatti riporta fatti; le cause le dicono le diagnostiche.** E' la regola che ha\r\n * rimesso in ordine la sezione dello schema, e vale oltre questo pannello. Qui a colonne zero si\r\n * leggeva «il backend non ha (ancora) propagato uno schema per questo nodo»: una **causa** dedotta\r\n * da un conteggio, falsa su ogni writeback sano — dove lo schema **e'** propagato e le colonne sono\r\n * zero per costruzione — e vera per caso solo sul nodo a schema ignoto. Tre stati erano collassati\r\n * in una frase.\r\n */\r\n\r\nimport { ChangeDetectionStrategy, Component, computed, inject, input } from '@angular/core';\r\n\r\nimport { dpeDiagnosticField } from '../../core/dpe-diagnostics.util';\r\nimport { DpeDocumentStore } from '../../core/dpe-document.store';\r\nimport { dpeFindNode } from '../../core/dpe-graph';\r\nimport { dpeNodeCategory } from '../../core/dpe-node-catalog';\r\nimport { Diagnostic } from '../../model/dpe-diagnostics.model';\r\n\r\n@Component({\r\n selector: 'dpe-node-facts',\r\n standalone: true,\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n template: `\r\n @if (isNotCompilable()) {\r\n <!--\r\n Tono deliberatamente **non** da errore: colore proprio (ne’ rosso ne’ giallo) e le stesse\r\n parole degli ispettori di Forecast e CustomNode, perche’ e’ lo stesso fatto detto nello\r\n stesso linguaggio. Chiedere di correggere qualcosa qui sarebbe una bugia.\r\n -->\r\n <p class=\"dpe-note dpe-note--not-compilable\">\r\n <span class=\"dpe-badge dpe-badge--not-compilable\">non compilabile</span>\r\n Il nodo e’ <strong>valido</strong>: non c’e’ niente da correggere. Il compilatore non lo\r\n traduce in questa versione, quindi per questo nodo\r\n <strong>non verra’ generato SQL</strong>.\r\n </p>\r\n }\r\n\r\n <section class=\"dpe-section\">\r\n <h4 class=\"dpe-section__title\">Schema in uscita</h4>\r\n <!--\r\n **Colonne da mostrare: si mostrano, e non si chiede altro.** Restano prima di tutto perche’\r\n sono cio’ che il backend ha mandato: un elenco non vuoto non si nasconde in nome di un\r\n booleano, nemmeno se i due si contraddicono. Sotto, i tre stati in cui non c’e’ nessun\r\n elenco.\r\n -->\r\n @if (columns().length > 0) {\r\n <div class=\"dpe-columns\">\r\n @for (column of columns(); track $index) {\r\n <span\r\n class=\"dpe-column\"\r\n [class.dpe-column--nullable]=\"column.isNullable\"\r\n [title]=\"column.type.kind + (column.isNullable ? ' · nullabile' : ' · non nulla')\"\r\n >\r\n {{ column.name }}\r\n </span>\r\n }\r\n </div>\r\n <p class=\"dpe-field__hint\">\r\n Tratteggiate le colonne nullabili. Lo schema arriva da <code>validate</code>: non lo\r\n calcoliamo qui.\r\n </p>\r\n } @else {\r\n <!--\r\n Nessun elenco: **tre stati, tre frasi**. Prima era una frase sola, e la frase era vera in\r\n uno solo dei tre — per caso. Su ogni writeback sano lo schema **e’** propagato e le\r\n colonne sono zero, quindi «il backend non ha propagato uno schema» diceva il falso\r\n proprio nel caso piu’ comune.\r\n\r\n La regola che divide i rami e’ quella del pannello schema e del canvas: si legge\r\n hasSchema, non si deduce dal numero di colonne.\r\n -->\r\n @if (!hasSchema()) {\r\n <!--\r\n L’unico ramo in cui «il backend non l’ha propagato» e’ vero: la derivazione non e’\r\n arrivata a una risposta, e l’elenco vuoto non afferma zero colonne (ADR 0041).\r\n\r\n Sta per primo perche’ **vince sul pozzo**: un writeback a valle di un ciclo non viene\r\n mai processato, e li’ non e’ che sappiamo che non ha colonne — e’ che non ci siamo\r\n arrivati.\r\n -->\r\n <p class=\"dpe-empty\">\r\n Schema ignoto: il backend non ha derivato uno schema per questo nodo.\r\n </p>\r\n } @else if (isSink()) {\r\n <!--\r\n Un pozzo ha zero colonne in uscita **per costruzione**, ed e’ un fatto vero, non\r\n un’ignoranza (ADR 0041 addendum 1). Non si parla di colonne mancanti: non manca niente.\r\n Stessa ragione per cui il canvas non ne mostra il conteggio.\r\n -->\r\n <p class=\"dpe-empty\">Un nodo pozzo non ha uno schema in uscita.</p>\r\n } @else {\r\n <!--\r\n Il fatto e basta. La **causa** e’ DPE_SCHEMA_EMPTY, che e’ un errore e che la\r\n diagnostica qui sotto spiega gia’: scriverla anche qui sarebbe una seconda copia di un\r\n fatto autorevole, e la seconda copia si scosta (ADR 0018).\r\n -->\r\n <p class=\"dpe-empty\">Nessuna colonna in uscita.</p>\r\n }\r\n }\r\n </section>\r\n\r\n @if (diagnostics().length > 0) {\r\n <section class=\"dpe-section\">\r\n <h4 class=\"dpe-section__title\">Rilievi su questo nodo</h4>\r\n @for (diagnostic of diagnostics(); track $index) {\r\n <p\r\n class=\"dpe-field__issue\"\r\n [class.dpe-field__issue--warning]=\"diagnostic.severity === 'Warning'\"\r\n [class.dpe-field__issue--info]=\"diagnostic.severity === 'Info'\"\r\n >\r\n <!--\r\n Il campo accanto al codice: due violazioni sullo stesso nodo sono **due** diagnostiche\r\n su due percorsi, e senza questo pezzo diventano due righe identiche a schermo. Il\r\n messaggio del contratto e’ deliberatamente uguale nei due casi — dice che il campo non\r\n e’ ammesso, non quale — quindi qui il campo e’ l’unica cosa che le distingue.\r\n -->\r\n <code>{{ diagnostic.code }}</code\r\n >@if (fieldOf(diagnostic); as campo) {\r\n · <code>{{ campo }}</code>\r\n }\r\n — {{ diagnostic.message }}\r\n </p>\r\n }\r\n </section>\r\n }\r\n `,\r\n})\r\nexport class DpeNodeFactsComponent {\r\n private readonly store = inject(DpeDocumentStore);\r\n\r\n /**\r\n * Il nodo di cui si mostrano i fatti. Arriva per input e non dalla selezione: chi monta questo\r\n * componente sa quale nodo sta guardando, e leggere qui la selezione lo farebbe cambiare\r\n * bersaglio sotto la dialog che lo contiene.\r\n */\r\n readonly nodeName = input.required<string>();\r\n\r\n /**\r\n * Il nodo e' valido ma **non compilabile** (**I10**). Due rami, ed e' la **stessa espressione**\r\n * che `DpeNodeInspectorBase.isNotCompilable` usa gia': il codice diagnostico\r\n * `DPE_NODE_NOT_COMPILABLE` raggruppato per nodo, **oppure** lo schema che dichiara\r\n * `isCompilable: false`. Servono entrambi perche' il backend puo' dirlo per due vie — un rilievo\r\n * sul nodo, o la riga di schema — e riconoscerne una sola vorrebbe dire tacere nell'altro caso.\r\n *\r\n * Qui non si giudica niente: la decisione e' del compilatore, questa e' la sua risposta.\r\n */\r\n readonly isNotCompilable = computed(\r\n () =>\r\n (this.store.diagnosticsByNode().get(this.nodeName())?.isNotCompilable ?? false) ||\r\n this.store.schemaByNode().get(this.nodeName())?.isCompilable === false,\r\n );\r\n\r\n readonly columns = computed(() => this.store.schemaByNode().get(this.nodeName())?.columns ?? []);\r\n\r\n /**\r\n * Se `columns` e' un **fatto** (ADR 0041). E' l'unica cosa che `hasSchema` dice: non dice che il\r\n * nodo sta bene, non dice che ha colonne, non dice che e' compilabile.\r\n *\r\n * `=== true` e non un confronto lasco: assente significa `false`, perche' un ospite che non\r\n * serializza i booleani falsi non manda affatto la proprieta' — e il verso prudente e' «non lo\r\n * so», non «zero colonne».\r\n */\r\n readonly hasSchema = computed(\r\n () => this.store.schemaByNode().get(this.nodeName())?.hasSchema === true,\r\n );\r\n\r\n /**\r\n * Il nodo e' un **pozzo**. Non si deduce da niente: si legge la collezione di appartenenza dal\r\n * documento e si chiede la categoria al catalogo dei nodi — la stessa che il view model del\r\n * canvas usa per decidere se disegnare la porta di uscita, e per la stessa domanda («da qui esce\r\n * qualcosa?»).\r\n *\r\n * Serve perche' uno schema di uscita **vuoto** significa due cose diverse su un pozzo e su un\r\n * nodo di mezzo, e la differenza non e' nei dati: e' nel tipo di nodo, che il backend ci ha gia'\r\n * detto qual e'.\r\n */\r\n readonly isSink = computed(() => {\r\n const located = dpeFindNode(this.store.definition(), this.nodeName());\r\n return located ? dpeNodeCategory(located.collection).isSink : false;\r\n });\r\n\r\n readonly diagnostics = computed(\r\n () => this.store.diagnosticsByNode().get(this.nodeName())?.diagnostics ?? [],\r\n );\r\n\r\n /**\r\n * Il campo in causa, dalla stessa funzione che gli ispettori usano per agganciare il rilievo al\r\n * controllo (`contracts.md` §1). Vuoto quando la diagnostica riguarda il nodo intero, e allora\r\n * la riga non mostra niente: e' un fatto sul nodo, non su un campo.\r\n */\r\n protected fieldOf(diagnostic: Diagnostic): string | undefined {\r\n return dpeDiagnosticField(diagnostic);\r\n }\r\n}\r\n","/**\r\n * `[dpeValue]` su un `<select>`.\r\n *\r\n * Perche' esiste, e non e' un vezzo: un `[value]` viene applicato **prima** che le `<option>`\r\n * generate da un `@for` esistano. Il browser scarta un valore che non corrisponde a nessuna\r\n * opzione, Angular non lo riapplica (il binding non e' cambiato) e la tendina resta sulla prima\r\n * voce — mostrando un valore diverso da quello del documento. Nessun errore, nessun avviso: solo\r\n * una tendina che mente. L'abbiamo visto sul selettore del dialetto e sulle funzioni di\r\n * aggregazione, dove `Sum` compariva come `Count`.\r\n *\r\n * La direttiva riallinea l'elemento al valore dichiarato a ogni giro di change detection, quindi\r\n * quando le opzioni compaiono la selezione si sistema da se'.\r\n */\r\n\r\nimport { AfterViewChecked, Directive, ElementRef, inject, input } from '@angular/core';\r\n\r\n@Directive({\r\n selector: 'select[dpeValue]',\r\n standalone: true,\r\n})\r\nexport class DpeSelectValueDirective implements AfterViewChecked {\r\n private readonly element = inject<ElementRef<HTMLSelectElement>>(ElementRef);\r\n\r\n readonly dpeValue = input<string | number | null | undefined>('');\r\n\r\n ngAfterViewChecked(): void {\r\n const select = this.element.nativeElement;\r\n const value = this.dpeValue();\r\n const normalized = value === null || value === undefined ? '' : String(value);\r\n if (select.value !== normalized) {\r\n select.value = normalized;\r\n }\r\n }\r\n}\r\n","/**\r\n * Base comune degli ispettori.\r\n *\r\n * `@Directive()` **senza selettore**: e' il solo modo in cui Angular riconosce come input\r\n * quelli dichiarati su una classe base ereditata da piu' componenti. Senza il decoratore ogni\r\n * ispettore darebbe NG8002 sul primo binding.\r\n *\r\n * Ogni ispettore riceve il nodo in **sola lettura** e scrive attraverso lo store: la\r\n * definizione e' un valore, le modifiche producono un nuovo valore. Nessun ispettore muta\r\n * l'oggetto che riceve.\r\n *\r\n * Quello che gli ispettori **non** fanno: non validano. Le sole verifiche locali ammesse sono\r\n * di forma del campo (obbligatorio non vuoto, un intero e' un intero) e servono al\r\n * comportamento del controllo, non a dire se la definizione va bene. Le diagnostiche mostrate\r\n * sono quelle del backend, con il loro codice e percorso.\r\n */\r\n\r\nimport { Directive, computed, inject, input } from '@angular/core';\r\n\r\nimport {\r\n DpeConditionalFields,\r\n dpeDeclares,\r\n dpePruneInapplicable,\r\n} from '../../core/dpe-conditional-fields';\r\nimport { dpeDiagnosticField, dpeDiagnosticTargetsItem } from '../../core/dpe-diagnostics.util';\r\nimport { DpeDocumentStore } from '../../core/dpe-document.store';\r\nimport { dpeFindNode, dpeNodeSourceReferences } from '../../core/dpe-graph';\r\nimport { DpeColumnInfo, DpeExpressionForm } from '../../model/dpe-api.model';\r\nimport { Diagnostic } from '../../model/dpe-diagnostics.model';\r\nimport { DpeAnyNode, DpeNodeCollectionName } from '../../model/dpe-definition.model';\r\n\r\n@Directive()\r\nexport abstract class DpeNodeInspectorBase<TNode extends DpeAnyNode = DpeAnyNode> {\r\n protected readonly store = inject(DpeDocumentStore);\r\n\r\n readonly nodeName = input.required<string>();\r\n readonly node = input.required<TNode>();\r\n\r\n /** La collezione di appartenenza: la sa l'ispettore, serve per scrivere nello store. */\r\n abstract readonly collection: DpeNodeCollectionName;\r\n\r\n /** Le diagnostiche ancorate a questo nodo, come le ha date il backend (**I6**). */\r\n readonly diagnostics = computed<readonly Diagnostic[]>(\r\n () => this.store.diagnosticsByNode().get(this.nodeName())?.diagnostics ?? [],\r\n );\r\n\r\n /**\r\n * Il nodo e' valido ma non compilabile (**I10**): l'ispettore lo dice in testa, perche' e'\r\n * l'informazione che spiega perche' non compare nessun SQL — e non c'e' niente da correggere.\r\n */\r\n readonly isNotCompilable = computed(\r\n () =>\r\n (this.store.diagnosticsByNode().get(this.nodeName())?.isNotCompilable ?? false) ||\r\n this.store.schemaByNode().get(this.nodeName())?.isCompilable === false,\r\n );\r\n\r\n /** Colonne prodotte dal nodo, secondo il backend. Mai calcolate qui (**I4**). */\r\n readonly outputColumns = computed<readonly DpeColumnInfo[]>(\r\n () => this.store.schemaByNode().get(this.nodeName())?.columns ?? [],\r\n );\r\n\r\n /**\r\n * La forma pretesa dai siti di espressione del nodo, **dal backend** (ADR 0031 §1).\r\n *\r\n * Si legge dallo **schema** e non dal catalogo delle formule, ed e' il punto della faccenda: la\r\n * forma e' cio' che la libreria ha derivato su questo nodo — come le colonne e la compilabilita'\r\n * qui sopra — mentre il catalogo e' cio' che il compilatore offre, e **arriva solo quando il\r\n * compilatore risponde**. Finche' viaggiava col catalogo, un ospite che dichiara di non\r\n * descrivere il proprio linguaggio non faceva chiamare la rotta e la forma non arrivava mai.\r\n *\r\n * Assente significa «questo nodo non ospita formule», ed e' uno stato normale: nessuna forma si\r\n * indovina dal tipo di nodo, che sarebbe una regola di dominio in TypeScript.\r\n *\r\n * Sta sulla base e non sui due ispettori che la usano perche' e' un campo dello stesso record da\r\n * cui gia' si leggono `outputColumns` e `isNotCompilable`: leggerlo due volte in due componenti\r\n * significherebbe due letture da tenere allineate.\r\n */\r\n readonly requiredForm = computed<DpeExpressionForm | undefined>(\r\n () => this.store.schemaByNode().get(this.nodeName())?.requiredForm ?? undefined,\r\n );\r\n\r\n /**\r\n * Colonne disponibili **in ingresso**: l'unione degli schemi dei nodi a monte, nell'ordine.\r\n * Arrivano dal backend; se la validazione non e' ancora tornata l'elenco e' vuoto e i campi\r\n * si digitano a mano — che e' meglio di un elenco indovinato in locale.\r\n */\r\n readonly inputColumns = computed<readonly DpeColumnInfo[]>(() => {\r\n const located = dpeFindNode(this.store.definition(), this.nodeName());\r\n if (!located) {\r\n return [];\r\n }\r\n const schemas = this.store.schemaByNode();\r\n const result: DpeColumnInfo[] = [];\r\n const seen = new Set<string>();\r\n for (const reference of dpeNodeSourceReferences(located)) {\r\n if (!reference.isEdge || !reference.sourceName) {\r\n continue;\r\n }\r\n for (const column of schemas.get(reference.sourceName)?.columns ?? []) {\r\n // Confronto insensibile al caso: le colonne si confrontano cosi' in tutto il sistema,\r\n // perche' cosi' si comportano i quattro database di destinazione.\r\n const key = column.name.toLowerCase();\r\n if (!seen.has(key)) {\r\n seen.add(key);\r\n result.push(column);\r\n }\r\n }\r\n }\r\n return result;\r\n });\r\n\r\n /** I parametri dichiarati sulla radice: servono a filtri e mapping dei writeback. */\r\n readonly parameters = computed(() => this.store.definition().parameters ?? []);\r\n\r\n /**\r\n * Le diagnostiche su un campo preciso, per mostrarle **accanto** al controllo.\r\n *\r\n * Il nome del campo si chiede a `dpeDiagnosticField` invece di leggere `path.field` qui:\r\n * `contracts.md` §1 nomina quella funzione come **il** punto in cui l'editor aggancia il rilievo\r\n * al controllo, e una funzione sola che sappia come una diagnostica nomina il proprio campo e'\r\n * la stessa ragione per cui esiste `DiagnosticArgumentKeys`. Il percorso e' gia' pubblicato in\r\n * tre forme diverse: il giorno in cui succede anche al campo, il posto da cambiare e' uno.\r\n */\r\n issuesOn(field: string): Diagnostic[] {\r\n return this.diagnostics().filter((diagnostic) => dpeDiagnosticField(diagnostic) === field);\r\n }\r\n\r\n hasIssueOn(field: string): boolean {\r\n return this.issuesOn(field).length > 0;\r\n }\r\n\r\n /**\r\n * Le diagnostiche su **una riga** di una collezione del nodo (`expressionFields[2].expression`).\r\n *\r\n * Esiste accanto a `issuesOn` perche' quella filtra per solo nome di campo, e in una collezione\r\n * il nome si ripete su ogni riga: l'errore della prima formula finirebbe sotto la terza.\r\n */\r\n issuesOnItem(collection: string, index: number, field: string): Diagnostic[] {\r\n return this.diagnostics().filter((diagnostic) =>\r\n dpeDiagnosticTargetsItem(diagnostic, collection, index, field),\r\n );\r\n }\r\n\r\n /**\r\n * «Il campo porta un valore», nel senso della regola di rendering di `frontend.md` §5-ter:\r\n * **un campo che porta un valore si rende anche fuori dalla propria condizione**.\r\n *\r\n * E' l'unica meta' della regola che serve anche **fuori** dalla tabella delle condizioni\r\n * (`core/dpe-conditional-fields.ts`): un campo dentro una riga di collezione non ha una voce\r\n * in tabella, perche' la ripulitura toglie chiavi del nodo, ma la sua resa segue la stessa\r\n * regola. Il predicato e' quello, non una seconda copia: qui si delega e basta.\r\n */\r\n protected declares(value: unknown): boolean {\r\n return dpeDeclares(value);\r\n }\r\n\r\n /** Applica una modifica al nodo. */\r\n protected patch(patch: Partial<TNode>): void {\r\n this.store.updateNode(this.collection, this.nodeName(), patch as never);\r\n }\r\n\r\n /**\r\n * Cambia un **discriminante** e toglie in un colpo solo i campi che con il valore nuovo non si\r\n * applicano piu', secondo la tabella delle condizioni del nodo.\r\n *\r\n * E' la seconda strada che legge quelle condizioni — la prima e' la resa di §5-ter — e legge la\r\n * **stessa** tabella: una riga aggiunta domani entra qui senza che nessuno la ricopi, che e' il\r\n * punto di `core/dpe-conditional-fields.ts`. La ripulitura resta una modifica come le altre e\r\n * rientra nell'annullamento globale, perche' passa da `patch`.\r\n */\r\n protected patchPruned<TConditions extends DpeConditionalFields<TNode>>(\r\n change: Partial<TNode>,\r\n conditions: TConditions,\r\n ): void {\r\n this.patch(dpePruneInapplicable(this.node(), change, conditions));\r\n }\r\n\r\n /**\r\n * Campo semplice: valore vuoto → chiave **rimossa**, non stringa vuota. Il backend\r\n * distingue assente da vuoto (`DPE_MODEL_REQUIRED_MISSING` contro un valore non valido), e\r\n * su Oracle la stringa vuota **e'** `NULL`: scriverla al posto di un'assenza cambia la\r\n * semantica invece di ripulire il campo.\r\n */\r\n protected setField(field: keyof TNode & string, value: unknown): void {\r\n const patch: Record<string, unknown> = {};\r\n patch[field] = value === '' || value === null ? undefined : value;\r\n this.patch(patch as Partial<TNode>);\r\n }\r\n\r\n /**\r\n * Interruttore il cui **default e' `false`**: spento → chiave **rimossa**, non `false` scritto.\r\n *\r\n * Due conseguenze, e la seconda e' quella che serve a §5-ter. La prima e' **I7**: chi accende e\r\n * rispegne per curiosita' non deve lasciare nel documento una chiave che non c'era. La seconda\r\n * e' che cosi' una casella **sa dire «assente»**: il controllo che rende un campo fuori dalla\r\n * propria condizione deve poterlo riportare all'assenza, e una casella che scrivesse `false`\r\n * resterebbe accesa-o-spenta senza mai togliere niente — resa e inerte, che e' il difetto\r\n * travestito da soluzione.\r\n *\r\n * **Non** si usa dove l'assenza vale `true` (`includesSelfValue`): li' `false` e' una\r\n * dichiarazione vera e toglierla direbbe il contrario.\r\n */\r\n protected setFlag(field: keyof TNode & string, value: boolean): void {\r\n const patch: Record<string, unknown> = {};\r\n patch[field] = value ? true : undefined;\r\n this.patch(patch as Partial<TNode>);\r\n }\r\n\r\n /** Numero da un campo di testo: vuoto o non numerico → assente, non `0`. */\r\n protected toNumber(value: string): number | undefined {\r\n if (value.trim() === '') {\r\n return undefined;\r\n }\r\n const parsed = Number(value);\r\n return Number.isFinite(parsed) ? parsed : undefined;\r\n }\r\n\r\n /** Lettura tipizzata di un valore da un evento di input, senza `any` nei template. */\r\n protected valueOf(event: Event): string {\r\n return (event.target as HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement).value;\r\n }\r\n\r\n protected checkedOf(event: Event): boolean {\r\n return (event.target as HTMLInputElement).checked;\r\n }\r\n}\r\n","/**\r\n * Ispettore dell'aggregazione.\r\n *\r\n * Lo schema di uscita e' `groupBy` (con i loro nomi) **piu'** gli alias delle funzioni, in\r\n * quest'ordine: l'ispettore mostra le due sezioni in quell'ordine per la stessa ragione.\r\n *\r\n * `sourceFieldName` e' ammesso vuoto **solo** per `Count` (conteggio righe). Il contratto lo\r\n * rappresenta con l'assenza e non con la stringa `\"*\"`, quindi il campo si disabilita invece\r\n * di suggerire un valore magico.\r\n */\r\n\r\nimport { ChangeDetectionStrategy, Component, computed } from '@angular/core';\r\n\r\nimport {\r\n AggregateField,\r\n AggregateFunction,\r\n AggregateNode,\r\n DpeNodeCollectionName,\r\n} from '../../model/dpe-definition.model';\r\nimport { DpeSelectValueDirective } from '../shared/dpe-select-value.directive';\r\nimport { DpeNodeInspectorBase } from './dpe-node-inspector-base';\r\n\r\nconst FUNCTIONS: readonly AggregateFunction[] = [\r\n 'Count',\r\n 'CountDistinct',\r\n 'Sum',\r\n 'Min',\r\n 'Max',\r\n 'Avg',\r\n 'StdDev',\r\n 'StdDevPop',\r\n 'Variance',\r\n 'VariancePop',\r\n];\r\n\r\n@Component({\r\n selector: 'dpe-aggregate-inspector',\r\n standalone: true,\r\n imports: [DpeSelectValueDirective],\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n template: `\r\n <div class=\"dpe-form\">\r\n <div class=\"dpe-field\">\r\n <label class=\"dpe-field__label\" for=\"agg-group\">Raggruppa per</label>\r\n <input\r\n id=\"agg-group\"\r\n type=\"text\"\r\n [value]=\"groupByText()\"\r\n placeholder=\"colonne separate da virgola\"\r\n (change)=\"onGroupBy(valueOf($event))\"\r\n />\r\n <p class=\"dpe-field__hint\">\r\n Vuoto = aggregazione globale, una sola riga in uscita.\r\n @if (inputColumns().length > 0) {\r\n Colonne disponibili: {{ columnHint() }}\r\n }\r\n </p>\r\n </div>\r\n\r\n <section class=\"dpe-section\">\r\n <h4 class=\"dpe-section__title\">Funzioni</h4>\r\n @if (fields().length === 0) {\r\n <p class=\"dpe-empty\">Nessuna funzione: il nodo non produce colonne calcolate.</p>\r\n } @else {\r\n <table class=\"dpe-table\">\r\n <thead>\r\n <tr>\r\n <th>Funzione</th>\r\n <th>Colonna</th>\r\n <th>Alias</th>\r\n <th></th>\r\n </tr>\r\n </thead>\r\n <tbody>\r\n @for (field of fields(); track $index) {\r\n <tr>\r\n <td>\r\n <select\r\n [dpeValue]=\"field.function\"\r\n [attr.aria-label]=\"'Funzione ' + ($index + 1)\"\r\n (change)=\"patchField($index, { function: asFunction(valueOf($event)) })\"\r\n >\r\n @for (option of functions; track $index) {\r\n <option [value]=\"option\">{{ option }}</option>\r\n }\r\n </select>\r\n </td>\r\n <td>\r\n <input\r\n type=\"text\"\r\n list=\"agg-columns\"\r\n [value]=\"field.sourceFieldName ?? ''\"\r\n [disabled]=\"field.function === 'Count'\"\r\n [attr.placeholder]=\"field.function === 'Count' ? 'tutte le righe' : ''\"\r\n [attr.aria-label]=\"'Colonna della funzione ' + ($index + 1)\"\r\n (change)=\"patchField($index, { sourceFieldName: valueOf($event) || undefined })\"\r\n />\r\n </td>\r\n <td>\r\n <input\r\n type=\"text\"\r\n [value]=\"field.alias\"\r\n [attr.aria-label]=\"'Alias della funzione ' + ($index + 1)\"\r\n (change)=\"patchField($index, { alias: valueOf($event) })\"\r\n />\r\n </td>\r\n <td>\r\n <button\r\n type=\"button\"\r\n class=\"dpe-btn dpe-btn--icon dpe-btn--danger\"\r\n aria-label=\"Rimuovi la funzione\"\r\n (click)=\"removeField($index)\"\r\n >\r\n ×\r\n </button>\r\n </td>\r\n </tr>\r\n }\r\n </tbody>\r\n </table>\r\n }\r\n <datalist id=\"agg-columns\">\r\n @for (column of inputColumns(); track $index) {\r\n <option [value]=\"column.name\">{{ column.type.kind }}</option>\r\n }\r\n </datalist>\r\n <div>\r\n <button type=\"button\" class=\"dpe-btn\" (click)=\"addField()\">Aggiungi funzione</button>\r\n </div>\r\n <p class=\"dpe-field__hint\">\r\n <code>StdDev</code> e le sue varianti non sono supportate da tutti i dialetti: se il\r\n dialetto scelto non le ha, la validazione lo dice con <code>DPE_DIALECT_UNSUPPORTED</code>.\r\n </p>\r\n </section>\r\n </div>\r\n `,\r\n})\r\nexport class DpeAggregateInspectorComponent extends DpeNodeInspectorBase<AggregateNode> {\r\n override readonly collection: DpeNodeCollectionName = 'aggregates';\r\n\r\n protected readonly functions = FUNCTIONS;\r\n\r\n readonly fields = computed<readonly AggregateField[]>(() => this.node().fields ?? []);\r\n\r\n readonly groupByText = computed(() => (this.node().groupBy ?? []).join(', '));\r\n\r\n readonly columnHint = computed(() => {\r\n const names = this.inputColumns().map((column) => column.name);\r\n return names.length > 10 ? `${names.slice(0, 10).join(', ')}, …` : names.join(', ');\r\n });\r\n\r\n protected asFunction(value: string): AggregateFunction {\r\n return value as AggregateFunction;\r\n }\r\n\r\n protected onGroupBy(raw: string): void {\r\n const columns = raw\r\n .split(',')\r\n .map((value) => value.trim())\r\n .filter((value) => value.length > 0);\r\n this.patch({ groupBy: columns.length > 0 ? columns : undefined });\r\n }\r\n\r\n protected addField(): void {\r\n this.patch({ fields: [...this.fields(), { function: 'Count', alias: '' }] });\r\n }\r\n\r\n protected removeField(index: number): void {\r\n this.patch({ fields: this.fields().filter((_, position) => position !== index) });\r\n }\r\n\r\n protected patchField(index: number, patch: Partial<AggregateField>): void {\r\n this.patch({\r\n fields: this.fields().map((field, position) =>\r\n position === index ? { ...field, ...patch } : field,\r\n ),\r\n });\r\n }\r\n}\r\n","/**\r\n * Ispettore dell'unione.\r\n *\r\n * Le sorgenti si collegano sul canvas: qui si aggiunge o si toglie una **posizione**, cioe' una\r\n * porta. Sono due gesti diversi e vanno in due posti diversi — togliere una porta con l'arco\r\n * attaccato sul canvas non si capirebbe.\r\n *\r\n * `allowsDisjointSchema` cambia il significato dell'unione (schema comune obbligatorio contro\r\n * unione con colonne nulle): l'etichetta lo dice, perche' il nome del campo da solo no.\r\n */\r\n\r\nimport { ChangeDetectionStrategy, Component, computed } from '@angular/core';\r\n\r\nimport { AppendNode, DpeNodeCollectionName } from '../../model/dpe-definition.model';\r\nimport { DpeNodeInspectorBase } from './dpe-node-inspector-base';\r\n\r\n@Component({\r\n selector: 'dpe-append-inspector',\r\n standalone: true,\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n template: `\r\n <div class=\"dpe-form\">\r\n <label class=\"dpe-check\">\r\n <input\r\n type=\"checkbox\"\r\n [checked]=\"node().allowsDisjointSchema ?? false\"\r\n (change)=\"setField('allowsDisjointSchema', checkedOf($event))\"\r\n />\r\n Ammetti schemi diversi\r\n </label>\r\n <p class=\"dpe-field__hint\">\r\n @if (node().allowsDisjointSchema) {\r\n Lo schema in uscita e’ l’unione degli alias: le colonne assenti in una sorgente\r\n diventano nulle.\r\n } @else {\r\n Gli schemi delle sorgenti devono coincidere per nome e tipo compatibile; se non\r\n coincidono, la validazione lo dice con <code>DPE_SCHEMA_INCOMPATIBLE_UNION</code>.\r\n }\r\n </p>\r\n\r\n <section class=\"dpe-section\">\r\n <h4 class=\"dpe-section__title\">Sorgenti ({{ sources().length }})</h4>\r\n <ol class=\"dpe-form\">\r\n @for (source of sources(); track $index) {\r\n <li class=\"dpe-row\">\r\n <span>{{ $index + 1 }}. {{ source || 'non collegata' }}</span>\r\n <button\r\n type=\"button\"\r\n class=\"dpe-btn dpe-btn--icon dpe-btn--danger\"\r\n [attr.aria-label]=\"'Rimuovi la posizione ' + ($index + 1)\"\r\n (click)=\"removeSource($index)\"\r\n >\r\n ×\r\n </button>\r\n </li>\r\n }\r\n </ol>\r\n <div>\r\n <button type=\"button\" class=\"dpe-btn\" (click)=\"addSource()\">Aggiungi posizione</button>\r\n </div>\r\n <p class=\"dpe-field__hint\">Le sorgenti si collegano trascinando un arco sulle porte in alto.</p>\r\n </section>\r\n </div>\r\n `,\r\n})\r\nexport class DpeAppendInspectorComponent extends DpeNodeInspectorBase<AppendNode> {\r\n override readonly collection: DpeNodeCollectionName = 'appends';\r\n\r\n readonly sources = computed<readonly string[]>(() => this.node().sources ?? []);\r\n\r\n protected addSource(): void {\r\n // Stringa vuota e non `undefined`: una posizione vuota **esiste** come porta, e serve un\r\n // elemento nell'array perche' il canvas la disegni.\r\n this.patch({ sources: [...this.sources(), ''] });\r\n }\r\n\r\n protected removeSource(index: number): void {\r\n this.patch({ sources: this.sources().filter((_, position) => position !== index) });\r\n }\r\n}\r\n","/**\r\n * Ispettore del gruppo atomico.\r\n *\r\n * Non e' un nodo del DAG: dichiara che alcune scritture vanno applicate dall'host **nella stessa\r\n * transazione**, nell'ordine dato. La libreria non apre transazioni — lo dice nel risultato e\r\n * l'host obbedisce — e l'avviso serve a non far credere che qui ci sia un `BEGIN`.\r\n *\r\n * I riferimenti sono a `WritebackNode` con destinazione `Database`: l'elenco e' chiuso a quelli,\r\n * perche' un gruppo atomico su un file non significherebbe niente.\r\n */\r\n\r\nimport { ChangeDetectionStrategy, Component, computed } from '@angular/core';\r\n\r\nimport {\r\n AtomicWritebackNode,\r\n AtomicWritebackRelationship,\r\n DpeNodeCollectionName,\r\n} from '../../model/dpe-definition.model';\r\nimport { DpeSelectValueDirective } from '../shared/dpe-select-value.directive';\r\nimport { DpeNodeInspectorBase } from './dpe-node-inspector-base';\r\n\r\n@Component({\r\n selector: 'dpe-atomic-writeback-inspector',\r\n standalone: true,\r\n imports: [DpeSelectValueDirective],\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n template: `\r\n <div class=\"dpe-form\">\r\n <p class=\"dpe-note\">\r\n La libreria non apre transazioni: questo gruppo <strong>dichiara</strong> che le scritture\r\n elencate vanno applicate insieme, e l’host lo esegue.\r\n </p>\r\n\r\n <div class=\"dpe-field\">\r\n <label class=\"dpe-field__label\" for=\"aw-sequence\">Sequenza</label>\r\n <input\r\n id=\"aw-sequence\"\r\n type=\"number\"\r\n min=\"1\"\r\n [value]=\"node().sequence\"\r\n [attr.aria-invalid]=\"hasIssueOn('sequence')\"\r\n (change)=\"setField('sequence', toNumber(valueOf($event)) ?? 1)\"\r\n />\r\n <p class=\"dpe-field__hint\">Nello stesso spazio di numerazione delle scritture.</p>\r\n </div>\r\n\r\n <section class=\"dpe-section\">\r\n <h4 class=\"dpe-section__title\">Relazioni</h4>\r\n @if (relationships().length === 0) {\r\n <p class=\"dpe-empty\">Nessuna relazione.</p>\r\n }\r\n @for (relationship of relationships(); track $index) {\r\n <div class=\"dpe-section\">\r\n <div class=\"dpe-row\">\r\n <div class=\"dpe-field\">\r\n <label class=\"dpe-field__label\" [attr.for]=\"'aw-parent-' + $index\">Scrittura padre</label>\r\n <select\r\n [id]=\"'aw-parent-' + $index\"\r\n [dpeValue]=\"relationship.parentWritebackName ?? ''\"\r\n (change)=\"patchRelationship($index, { parentWritebackName: valueOf($event) || undefined })\"\r\n >\r\n <option value=\"\">—</option>\r\n @for (writeback of databaseWritebacks(); track $index) {\r\n <option [value]=\"writeback.name\">{{ writeback.name }}</option>\r\n }\r\n </select>\r\n </div>\r\n <div class=\"dpe-field\">\r\n <label class=\"dpe-field__label\" [attr.for]=\"'aw-child-' + $index\">Scrittura figlia</label>\r\n <select\r\n [id]=\"'aw-child-' + $index\"\r\n [dpeValue]=\"relationship.childWritebackName ?? ''\"\r\n (change)=\"patchRelationship($index, { childWritebackName: valueOf($event) || undefined })\"\r\n >\r\n <option value=\"\">—</option>\r\n @for (writeback of databaseWritebacks(); track $index) {\r\n <option [value]=\"writeback.name\">{{ writeback.name }}</option>\r\n }\r\n </select>\r\n </div>\r\n <div class=\"dpe-field\">\r\n <label class=\"dpe-field__label\" [attr.for]=\"'aw-seq-' + $index\">Ordine</label>\r\n <input\r\n [id]=\"'aw-seq-' + $index\"\r\n type=\"number\"\r\n min=\"1\"\r\n [value]=\"relationship.sequence\"\r\n (change)=\"patchRelationship($index, { sequence: toNumber(valueOf($event)) ?? 1 })\"\r\n />\r\n </div>\r\n </div>\r\n <div class=\"dpe-row\">\r\n <div class=\"dpe-field\">\r\n <label class=\"dpe-field__label\" [attr.for]=\"'aw-pfield-' + $index\">Campo padre</label>\r\n <input\r\n [id]=\"'aw-pfield-' + $index\"\r\n type=\"text\"\r\n [value]=\"relationship.parentFieldName ?? ''\"\r\n (change)=\"patchRelationship($index, { parentFieldName: valueOf($event) || undefined })\"\r\n />\r\n </div>\r\n <div class=\"dpe-field\">\r\n <label class=\"dpe-field__label\" [attr.for]=\"'aw-cfield-' + $index\">Campo figlio</label>\r\n <input\r\n [id]=\"'aw-cfield-' + $index\"\r\n type=\"text\"\r\n [value]=\"relationship.childFieldName ?? ''\"\r\n (change)=\"patchRelationship($index, { childFieldName: valueOf($event) || undefined })\"\r\n />\r\n </div>\r\n <div class=\"dpe-field\">\r\n <label class=\"dpe-field__label\" [attr.for]=\"'aw-rel-' + $index\">Relazione</label>\r\n <input\r\n [id]=\"'aw-rel-' + $index\"\r\n type=\"text\"\r\n [value]=\"relationship.relationshipName ?? ''\"\r\n (change)=\"patchRelationship($index, { relationshipName: valueOf($event) || undefined })\"\r\n />\r\n </div>\r\n <button\r\n type=\"button\"\r\n class=\"dpe-btn dpe-btn--danger\"\r\n [attr.aria-label]=\"'Rimuovi la relazione ' + ($index + 1)\"\r\n (click)=\"removeRelationship($index)\"\r\n >\r\n Rimuovi\r\n </button>\r\n </div>\r\n </div>\r\n }\r\n <div>\r\n <button type=\"button\" class=\"dpe-btn\" (click)=\"addRelationship()\">Aggiungi relazione</button>\r\n </div>\r\n </section>\r\n </div>\r\n `,\r\n})\r\nexport class DpeAtomicWritebackInspectorComponent extends DpeNodeInspectorBase<AtomicWritebackNode> {\r\n override readonly collection: DpeNodeCollectionName = 'atomicWritebacks';\r\n\r\n readonly relationships = computed<readonly AtomicWritebackRelationship[]>(\r\n () => this.node().relationships ?? [],\r\n );\r\n\r\n readonly databaseWritebacks = computed(() =>\r\n (this.store.definition().writebacks ?? []).filter((writeback) => writeback.target === 'Database'),\r\n );\r\n\r\n protected addRelationship(): void {\r\n const nextSequence =\r\n this.relationships().reduce((max, relationship) => Math.max(max, relationship.sequence), 0) + 1;\r\n this.patch({ relationships: [...this.relationships(), { sequence: nextSequence }] });\r\n }\r\n\r\n protected removeRelationship(index: number): void {\r\n this.patch({\r\n relationships: this.relationships().filter((_, position) => position !== index),\r\n });\r\n }\r\n\r\n protected patchRelationship(index: number, patch: Partial<AtomicWritebackRelationship>): void {\r\n this.patch({\r\n relationships: this.relationships().map((relationship, position) =>\r\n position === index ? { ...relationship, ...patch } : relationship,\r\n ),\r\n });\r\n }\r\n}\r\n","/**\r\n * Editor di un `ColumnType`: i sei tipi logici e i loro attributi condizionali.\r\n *\r\n * I campi che compaiono dipendono dal `kind`, e la dipendenza e' quella del contratto:\r\n * `length` per `String`, `precision`/`scale` per `Number`, `allowedValues` per `Enum`. Mostrare\r\n * sempre tutto renderebbe rappresentabile un tipo che il backend rifiuta; mostrare solo cio'\r\n * che serve rende quello stato non rappresentabile — **tranne quando lo stato c'e' gia'**\r\n * (`frontend.md` §5-ter): un attributo che porta un valore si rende anche sul tipo che non lo\r\n * prevede, altrimenti `onKind` lo azzera solo per chi passa di qui e un documento scritto\r\n * altrove se lo tiene per sempre.\r\n *\r\n * Cosa **non** fa: non decide se `length` sia obbligatorio in questo punto della definizione —\r\n * lo e' nei campi *dichiarati* e non altrove, e a dirlo e' la validazione\r\n * (`DPE_MODEL_CONDITIONAL_REQUIRED`).\r\n */\r\n\r\nimport { ChangeDetectionStrategy, Component, computed, input, output } from '@angular/core';\r\n\r\nimport { ColumnType, LogicalType } from '../../model/dpe-definition.model';\r\n\r\nconst LOGICAL_TYPES: readonly LogicalType[] = [\r\n 'String',\r\n 'Number',\r\n 'Integer',\r\n 'DateTime',\r\n 'Boolean',\r\n 'Enum',\r\n];\r\n\r\nimport { DpeSelectValueDirective } from './dpe-select-value.directive';\r\n\r\n@Component({\r\n selector: 'dpe-column-type-editor',\r\n standalone: true,\r\n imports: [DpeSelectValueDirective],\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n template: `\r\n <div class=\"dpe-row\">\r\n <div class=\"dpe-field\">\r\n <label class=\"dpe-field__label\" [attr.for]=\"controlId() + '-kind'\">Tipo</label>\r\n <select [id]=\"controlId() + '-kind'\" [dpeValue]=\"kind()\" (change)=\"onKind(text($event))\">\r\n @for (option of types; track $index) {\r\n <option [value]=\"option\">{{ option }}</option>\r\n }\r\n </select>\r\n </div>\r\n\r\n <!--\r\n Gli attributi si rendono per il proprio tipo **oppure quando portano un valore**\r\n (frontend.md §5-ter): una lunghezza arrivata su un DateTime da un documento scritto\r\n altrove deve restare visibile e svuotabile, che qui vuol dire cancellare il numero. Con\r\n l’attributo nascosto resterebbe nel documento per sempre, e nessuno lo segnala.\r\n -->\r\n @if (showsLength()) {\r\n <div class=\"dpe-field\">\r\n <label class=\"dpe-field__label\" [attr.for]=\"controlId() + '-length'\">Lunghezza</label>\r\n <input\r\n type=\"number\"\r\n min=\"1\"\r\n [id]=\"controlId() + '-length'\"\r\n [value]=\"value()?.length ?? ''\"\r\n (change)=\"onNumber('length', text($event))\"\r\n />\r\n </div>\r\n }\r\n\r\n @if (showsPrecision()) {\r\n <div class=\"dpe-field\">\r\n <label class=\"dpe-field__label\" [attr.for]=\"controlId() + '-precision'\">Precisione</label>\r\n <input\r\n type=\"number\"\r\n min=\"1\"\r\n [id]=\"controlId() + '-precision'\"\r\n [value]=\"value()?.precision ?? ''\"\r\n (change)=\"onNumber('precision', text($event))\"\r\n />\r\n </div>\r\n <div class=\"dpe-field\">\r\n <label class=\"dpe-field__label\" [attr.for]=\"controlId() + '-scale'\">Decimali</label>\r\n <input\r\n type=\"number\"\r\n min=\"0\"\r\n [id]=\"controlId() + '-scale'\"\r\n [value]=\"value()?.scale ?? ''\"\r\n (change)=\"onNumber('scale', text($event))\"\r\n />\r\n </div>\r\n }\r\n </div>\r\n\r\n @if (showsAllowedValues()) {\r\n <div class=\"dpe-field\">\r\n <label class=\"dpe-field__label\" [attr.for]=\"controlId() + '-values'\">Valori ammessi</label>\r\n <input\r\n type=\"text\"\r\n [id]=\"controlId() + '-values'\"\r\n [value]=\"allowedValues()\"\r\n placeholder=\"separati da virgola\"\r\n (change)=\"onAllowedValues(text($event))\"\r\n />\r\n </div>\r\n }\r\n `,\r\n})\r\nexport class DpeColumnTypeEditorComponent {\r\n readonly value = input<ColumnType | undefined>(undefined);\r\n readonly controlId = input('dpe-type');\r\n readonly valueChange = output<ColumnType>();\r\n\r\n protected readonly types = LOGICAL_TYPES;\r\n\r\n readonly kind = computed<LogicalType>(() => this.value()?.kind ?? 'String');\r\n\r\n readonly allowedValues = computed(() => (this.value()?.allowedValues ?? []).join(', '));\r\n\r\n /*\r\n * Gli attributi condizionali del tipo, nella forma di `frontend.md` §5-ter: **il tipo li\r\n * prevede oppure il valore c'e'**. Questo componente non estende `DpeNodeInspectorBase` — non\r\n * e' un ispettore, e' un controllo — quindi la condizione e' scritta qui, ed e' banale perche'\r\n * sono tre numeri e una lista: assente vale assente e basta.\r\n */\r\n readonly showsLength = computed(\r\n () => this.kind() === 'String' || this.value()?.length !== undefined,\r\n );\r\n\r\n readonly showsPrecision = computed(\r\n () =>\r\n this.kind() === 'Number' ||\r\n this.value()?.precision !== undefined ||\r\n this.value()?.scale !== undefined,\r\n );\r\n\r\n readonly showsAllowedValues = computed(\r\n () => this.kind() === 'Enum' || (this.value()?.allowedValues?.length ?? 0) > 0,\r\n );\r\n\r\n protected onKind(kind: string): void {\r\n // Cambiare tipo **azzera** gli attributi dell'altro: una `length` rimasta su un `Number`\r\n // sarebbe un campo che il backend ignora oggi e rifiuta domani.\r\n this.valueChange.emit({ kind: kind as LogicalType });\r\n }\r\n\r\n protected onNumber(field: 'length' | 'precision' | 'scale', raw: string): void {\r\n const parsed = raw.trim() === '' ? undefined : Number(raw);\r\n this.valueChange.emit({\r\n ...(this.value() ?? { kind: this.kind() }),\r\n [field]: Number.isFinite(parsed) ? parsed : undefined,\r\n });\r\n }\r\n\r\n protected onAllowedValues(raw: string): void {\r\n const values = raw\r\n .split(',')\r\n .map((value) => value.trim())\r\n .filter((value) => value.length > 0);\r\n this.valueChange.emit({\r\n ...(this.value() ?? { kind: 'Enum' }),\r\n allowedValues: values.length > 0 ? values : undefined,\r\n });\r\n }\r\n\r\n protected text(event: Event): string {\r\n return (event.target as HTMLInputElement | HTMLSelectElement).value;\r\n }\r\n}\r\n","/**\r\n * Ispettore del nodo personalizzato.\r\n *\r\n * `outputFields` non e' un campo come gli altri: **e' l'unica cosa che tiene in piedi lo schema\r\n * a valle** (**I4**). Senza uno schema dichiarato la propagazione si interrompe e tutto cio' che\r\n * sta dopo diventa invalidabile — quindi l'avviso non e' generico, dice esattamente questo.\r\n *\r\n * Come il `Forecast`, e' modellato e non compilabile in v1.\r\n */\r\n\r\nimport { ChangeDetectionStrategy, Component, computed } from '@angular/core';\r\n\r\nimport {\r\n ColumnType,\r\n CustomNode,\r\n CustomNodeParameter,\r\n DeclaredField,\r\n DpeNodeCollectionName,\r\n} from '../../model/dpe-definition.model';\r\nimport { DpeColumnTypeEditorComponent } from '../shared/dpe-column-type-editor.component';\r\nimport { DpeNodeInspectorBase } from './dpe-node-inspector-base';\r\n\r\n@Component({\r\n selector: 'dpe-custom-node-inspector',\r\n standalone: true,\r\n imports: [DpeColumnTypeEditorComponent],\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n template: `\r\n <div class=\"dpe-form\">\r\n <p class=\"dpe-note dpe-note--not-compilable\">\r\n Nodo modellato per intero ma <strong>non traducibile in SQL</strong> nella versione 1.\r\n </p>\r\n\r\n <div class=\"dpe-row\">\r\n <div class=\"dpe-field\">\r\n <label class=\"dpe-field__label\" for=\"cn-extension\">Estensione</label>\r\n <input\r\n id=\"cn-extension\"\r\n type=\"text\"\r\n [value]=\"node().extensionName ?? ''\"\r\n (change)=\"setField('extensionName', valueOf($event))\"\r\n />\r\n </div>\r\n <div class=\"dpe-field\">\r\n <label class=\"dpe-field__label\" for=\"cn-namespace\">Namespace</label>\r\n <input\r\n id=\"cn-namespace\"\r\n type=\"text\"\r\n [value]=\"node().extensionNamespace ?? ''\"\r\n (change)=\"setField('extensionNamespace', valueOf($event))\"\r\n />\r\n </div>\r\n </div>\r\n\r\n <section class=\"dpe-section\">\r\n <h4 class=\"dpe-section__title\">Sorgenti ({{ sources().length }})</h4>\r\n @for (source of sources(); track $index) {\r\n <div class=\"dpe-row\">\r\n <span>{{ $index + 1 }}. {{ source || 'non collegata' }}</span>\r\n <button\r\n type=\"button\"\r\n class=\"dpe-btn dpe-btn--icon dpe-btn--danger\"\r\n [attr.aria-label]=\"'Rimuovi la posizione ' + ($index + 1)\"\r\n (click)=\"removeSource($index)\"\r\n >\r\n ×\r\n </button>\r\n </div>\r\n }\r\n <div>\r\n <button type=\"button\" class=\"dpe-btn\" (click)=\"addSource()\">Aggiungi posizione</button>\r\n </div>\r\n </section>\r\n\r\n <section class=\"dpe-section\">\r\n <h4 class=\"dpe-section__title\">Parametri dell’estensione</h4>\r\n @for (parameter of nodeParameters(); track $index) {\r\n <div class=\"dpe-row\">\r\n <input\r\n type=\"text\"\r\n [value]=\"parameter.name\"\r\n [attr.aria-label]=\"'Nome del parametro ' + ($index + 1)\"\r\n (change)=\"patchParameter($index, { name: valueOf($event) })\"\r\n />\r\n <input\r\n type=\"text\"\r\n [value]=\"parameter.value\"\r\n [attr.aria-label]=\"'Valore del parametro ' + ($index + 1)\"\r\n (change)=\"patchParameter($index, { value: valueOf($event) })\"\r\n />\r\n <button\r\n type=\"button\"\r\n class=\"dpe-btn dpe-btn--icon dpe-btn--danger\"\r\n aria-label=\"Rimuovi il parametro\"\r\n (click)=\"removeParameter($index)\"\r\n >\r\n ×\r\n </button>\r\n </div>\r\n }\r\n <div>\r\n <button type=\"button\" class=\"dpe-btn\" (click)=\"addParameter()\">Aggiungi parametro</button>\r\n </div>\r\n </section>\r\n\r\n <section class=\"dpe-section\">\r\n <h4 class=\"dpe-section__title\">Schema in uscita</h4>\r\n <p class=\"dpe-note dpe-note--warning\">\r\n Va dichiarato qui: non esiste un registro delle estensioni da cui ricavarlo. Se manca,\r\n la propagazione dello schema si interrompe e <strong>tutto cio’ che sta a valle</strong>\r\n diventa invalidabile.\r\n </p>\r\n @for (field of outputFields(); track $index) {\r\n <div class=\"dpe-section\">\r\n <div class=\"dpe-row\">\r\n <div class=\"dpe-field\">\r\n <label class=\"dpe-field__label\" [attr.for]=\"'cn-field-' + $index\">Nome</label>\r\n <input\r\n [id]=\"'cn-field-' + $index\"\r\n type=\"text\"\r\n [value]=\"field.name\"\r\n (change)=\"patchOutput($index, { name: valueOf($event) })\"\r\n />\r\n </div>\r\n <button\r\n type=\"button\"\r\n class=\"dpe-btn dpe-btn--danger\"\r\n [attr.aria-label]=\"'Rimuovi la colonna ' + field.name\"\r\n (click)=\"removeOutput($index)\"\r\n >\r\n Rimuovi\r\n </button>\r\n </div>\r\n <dpe-column-type-editor\r\n [value]=\"field.type\"\r\n [controlId]=\"'cn-type-' + $index\"\r\n (valueChange)=\"patchOutput($index, { type: $event })\"\r\n />\r\n </div>\r\n }\r\n <div>\r\n <button type=\"button\" class=\"dpe-btn\" (click)=\"addOutput()\">Aggiungi colonna</button>\r\n </div>\r\n </section>\r\n </div>\r\n `,\r\n})\r\nexport class DpeCustomNodeInspectorComponent extends DpeNodeInspectorBase<CustomNode> {\r\n override readonly collection: DpeNodeCollectionName = 'customNodes';\r\n\r\n readonly sources = computed<readonly string[]>(() => this.node().sources ?? []);\r\n readonly nodeParameters = computed<readonly CustomNodeParameter[]>(\r\n () => this.node().parameters ?? [],\r\n );\r\n readonly outputFields = computed<readonly DeclaredField[]>(() => this.node().outputFields ?? []);\r\n\r\n protected addSource(): void {\r\n this.patch({ sources: [...this.sources(), ''] });\r\n }\r\n\r\n protected removeSource(index: number): void {\r\n this.patch({ sources: this.sources().filter((_, position) => position !== index) });\r\n }\r\n\r\n protected addParameter(): void {\r\n this.patch({ parameters: [...this.nodeParameters(), { name: '', value: '' }] });\r\n }\r\n\r\n protected removeParameter(index: number): void {\r\n this.patch({\r\n parameters: this.nodeParameters().filter((_, position) => position !== index),\r\n });\r\n }\r\n\r\n protected patchParameter(index: number, patch: Partial<CustomNodeParameter>): void {\r\n this.patch({\r\n parameters: this.nodeParameters().map((parameter, position) =>\r\n position === index ? { ...parameter, ...patch } : parameter,\r\n ),\r\n });\r\n }\r\n\r\n protected addOutput(): void {\r\n this.patch({\r\n outputFields: [...this.outputFields(), { name: '', type: { kind: 'String' } }],\r\n });\r\n }\r\n\r\n protected removeOutput(index: number): void {\r\n this.patch({ outputFields: this.outputFields().filter((_, position) => position !== index) });\r\n }\r\n\r\n protected patchOutput(index: number, patch: Partial<DeclaredField> & { type?: ColumnType }): void {\r\n this.patch({\r\n outputFields: this.outputFields().map((field, position) =>\r\n position === index ? { ...field, ...patch } : field,\r\n ),\r\n });\r\n }\r\n}\r\n","/**\r\n * Ispettore della sorgente.\r\n *\r\n * I campi che compaiono dipendono dal `kind`: `tableName` per `Table`, le opzioni CSV per\r\n * `Csv`, il nome del dataset per `ExternalDataset`. Il `declaredType` di un campo si mostra\r\n * **solo** dove serve — per `Table` il tipo lo da' il provider di metadati e quello dichiarato\r\n * viene ignorato: mostrarlo farebbe credere di poterlo decidere.\r\n *\r\n * «Dipendono dal `kind`» con l'eccezione normativa di `frontend.md` §5-ter, che vale per tutti e\r\n * quattro: **un campo che porta un valore si rende anche fuori dalla propria condizione**, e con\r\n * un controllo capace di riportarlo all'assenza. Nessuno dei quattro e' diagnosticato fuori\r\n * condizione, il che li rende il caso peggiore e non quello trascurabile: un valore che nessuno\r\n * segnala, che nessuna schermata mostra, e che il primo cambio di `kind` rimette in gioco.\r\n *\r\n * I tre esiti del provider (ADR 0004) restano distinti: tabella dichiarata, dichiarata **senza\r\n * campi**, sconosciuta. Nel secondo caso i nomi dei campi si digitano a mano e non c'e' niente\r\n * da correggere; nel terzo la diagnostica autorevole e' `DPE_REF_UNKNOWN_TABLE` del backend.\r\n */\r\n\r\nimport { ChangeDetectionStrategy, Component, computed, effect, inject } from '@angular/core';\r\n\r\nimport {\r\n DPE_DATA_SOURCE_CONDITIONS,\r\n dpeFieldVisibility,\r\n} from '../../core/dpe-conditional-fields';\r\nimport { DpeMetadataStore } from '../../core/dpe-metadata.store';\r\nimport {\r\n ColumnType,\r\n CsvDelimiter,\r\n DataSourceField,\r\n DataSourceKind,\r\n DataSourceNode,\r\n DpeNodeCollectionName,\r\n} from '../../model/dpe-definition.model';\r\nimport { DpeColumnTypeEditorComponent } from '../shared/dpe-column-type-editor.component';\r\nimport { DpeSelectValueDirective } from '../shared/dpe-select-value.directive';\r\nimport { DpeNodeInspectorBase } from './dpe-node-inspector-base';\r\n\r\nconst KINDS: readonly DataSourceKind[] = ['Table', 'Csv', 'ExternalDataset'];\r\nconst DELIMITERS: readonly CsvDelimiter[] = ['Comma', 'Semicolon', 'Tab', 'Pipe', 'Caret', 'Backquote'];\r\n\r\n@Component({\r\n selector: 'dpe-data-source-inspector',\r\n standalone: true,\r\n imports: [DpeColumnTypeEditorComponent, DpeSelectValueDirective],\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n template: `\r\n <div class=\"dpe-form\">\r\n <div class=\"dpe-field\">\r\n <label class=\"dpe-field__label\" for=\"ds-kind\">Tipo di sorgente</label>\r\n <select id=\"ds-kind\" [dpeValue]=\"node().kind\" (change)=\"setField('kind', valueOf($event))\">\r\n @for (kind of kinds; track $index) {\r\n <option [value]=\"kind\">{{ kind }}</option>\r\n }\r\n </select>\r\n </div>\r\n\r\n <!--\r\n Tre blocchi indipendenti e non uno switch: la regola di rendering di frontend.md §5-ter\r\n e’ normativa e vale qui come sull’ispettore della scrittura — **un campo che porta un\r\n valore si rende anche fuori dalla propria condizione**. Con uno switch il nome di tabella\r\n di una sorgente diventata Csv non lo vedrebbe piu’ nessuno e non lo potrebbe togliere\r\n nessuno, e qui il backend **non dice niente**: fuori da Table quel valore non e’ vietato,\r\n e’ ignorato. Vuoto e non applicabile resta nascosto, cosi’ il caso normale non guadagna\r\n rumore.\r\n -->\r\n @if (shows.tableName()) {\r\n <div class=\"dpe-field\">\r\n <label class=\"dpe-field__label\" for=\"ds-table\">Tabella</label>\r\n <input\r\n id=\"ds-table\"\r\n type=\"text\"\r\n list=\"ds-table-options\"\r\n [value]=\"node().tableName ?? ''\"\r\n [attr.aria-invalid]=\"hasIssueOn('tableName')\"\r\n (change)=\"setField('tableName', valueOf($event))\"\r\n />\r\n <!--\r\n Elenco proposto (datalist) e non tendina chiusa: le tabelle possono essere molte e\r\n il nome resta digitabile — un catalogo incompleto non deve impedire di scrivere.\r\n -->\r\n <datalist id=\"ds-table-options\">\r\n @for (table of tables(); track $index) {\r\n <option [value]=\"table.name\">{{ table.description || table.name }}</option>\r\n }\r\n </datalist>\r\n <p class=\"dpe-field__hint\">{{ tableHint() }}</p>\r\n @for (issue of issuesOn('tableName'); track $index) {\r\n <p class=\"dpe-field__issue\">{{ issue.code }} — {{ issue.message }}</p>\r\n }\r\n </div>\r\n }\r\n\r\n @if (shows.csvOptions()) {\r\n <div class=\"dpe-field\">\r\n <label class=\"dpe-field__label\" for=\"ds-location\">Posizione del file</label>\r\n <input\r\n id=\"ds-location\"\r\n type=\"text\"\r\n [value]=\"node().csvOptions?.location ?? ''\"\r\n (change)=\"patchCsv({ location: valueOf($event) })\"\r\n />\r\n <p class=\"dpe-field__hint\">Identificatore opaco: lo risolve l’host, non la libreria.</p>\r\n </div>\r\n <div class=\"dpe-row\">\r\n <div class=\"dpe-field\">\r\n <label class=\"dpe-field__label\" for=\"ds-delimiter\">Separatore</label>\r\n <select\r\n id=\"ds-delimiter\"\r\n [dpeValue]=\"node().csvOptions?.delimiter ?? 'Comma'\"\r\n (change)=\"patchCsv({ delimiter: valueOf($event) })\"\r\n >\r\n @for (delimiter of delimiters; track $index) {\r\n <option [value]=\"delimiter\">{{ delimiter }}</option>\r\n }\r\n </select>\r\n </div>\r\n <label class=\"dpe-check\">\r\n <input\r\n type=\"checkbox\"\r\n [checked]=\"node().csvOptions?.hasHeaderRow ?? true\"\r\n (change)=\"patchCsv({ hasHeaderRow: checkedOf($event) })\"\r\n />\r\n Prima riga di intestazione\r\n </label>\r\n <!--\r\n Il comando c’e’ quando **l’oggetto c’e’**, dentro e fuori dalla propria condizione:\r\n una tendina e una casella sanno proporre un altro valore, non sanno dire «assente»,\r\n e senza questo comando le opzioni rimaste su una sorgente diventata Table sarebbero\r\n rese e inerti — cioe’ visibili e ancora non cancellabili.\r\n -->\r\n @if (node().csvOptions) {\r\n <button type=\"button\" class=\"dpe-btn\" (click)=\"removeCsvOptions()\">\r\n Rimuovi le opzioni CSV\r\n </button>\r\n }\r\n </div>\r\n }\r\n\r\n @if (shows.externalDatasetName()) {\r\n <div class=\"dpe-field\">\r\n <label class=\"dpe-field__label\" for=\"ds-dataset\">Dataset esterno</label>\r\n <input\r\n id=\"ds-dataset\"\r\n type=\"text\"\r\n [value]=\"node().externalDatasetName ?? ''\"\r\n (change)=\"setField('externalDatasetName', valueOf($event))\"\r\n />\r\n </div>\r\n }\r\n\r\n <section class=\"dpe-section\">\r\n <h4 class=\"dpe-section__title\">Campi</h4>\r\n @if (fields().length === 0) {\r\n <p class=\"dpe-empty\">Nessun campo: la sorgente non produce colonne.</p>\r\n } @else {\r\n <table class=\"dpe-table\">\r\n <thead>\r\n <tr>\r\n <th>Nome</th>\r\n <th>Alias</th>\r\n @if (showsTypes()) {\r\n <th>Tipo</th>\r\n }\r\n <th>PK</th>\r\n <th></th>\r\n </tr>\r\n </thead>\r\n <tbody>\r\n @for (field of fields(); track $index) {\r\n <tr>\r\n <td>\r\n <input\r\n type=\"text\"\r\n list=\"ds-field-options\"\r\n [value]=\"field.name\"\r\n [attr.aria-label]=\"'Nome del campo ' + ($index + 1)\"\r\n (change)=\"patchField($index, { name: valueOf($event) })\"\r\n />\r\n </td>\r\n <td>\r\n <input\r\n type=\"text\"\r\n [value]=\"field.alias ?? ''\"\r\n [attr.aria-label]=\"'Alias del campo ' + ($index + 1)\"\r\n (change)=\"patchField($index, { alias: valueOf($event) || undefined })\"\r\n />\r\n </td>\r\n @if (showsTypes()) {\r\n <td>\r\n <dpe-column-type-editor\r\n [value]=\"field.declaredType\"\r\n [controlId]=\"'ds-field-' + $index\"\r\n (valueChange)=\"patchField($index, { declaredType: $event })\"\r\n />\r\n <!--\r\n L’editor di tipo emette **sempre** un tipo: non sa dire «nessuno». Su una\r\n sorgente Table il tipo dichiarato il backend lo ignora, quindi qui serve\r\n il comando che lo toglie, altrimenti la colonna comparirebbe senza poter\r\n tornare all’assenza. C’e’ quando il tipo c’e’, dentro e fuori condizione.\r\n -->\r\n @if (field.declaredType) {\r\n <button\r\n type=\"button\"\r\n class=\"dpe-btn\"\r\n [attr.aria-label]=\"'Rimuovi il tipo dichiarato del campo ' + ($index + 1)\"\r\n (click)=\"patchField($index, { declaredType: undefined })\"\r\n >\r\n Rimuovi il tipo\r\n </button>\r\n }\r\n </td>\r\n }\r\n <td>\r\n <input\r\n type=\"checkbox\"\r\n [checked]=\"field.isPrimaryKey ?? false\"\r\n [attr.aria-label]=\"'Chiave primaria del campo ' + ($index + 1)\"\r\n (change)=\"patchField($index, { isPrimaryKey: checkedOf($event) })\"\r\n />\r\n </td>\r\n <td>\r\n <button\r\n type=\"button\"\r\n class=\"dpe-btn dpe-btn--icon dpe-btn--danger\"\r\n [attr.aria-label]=\"'Rimuovi il campo ' + field.name\"\r\n (click)=\"removeField($index)\"\r\n >\r\n ×\r\n </button>\r\n </td>\r\n </tr>\r\n }\r\n </tbody>\r\n </table>\r\n }\r\n <datalist id=\"ds-field-options\">\r\n @for (candidate of providerFields(); track $index) {\r\n <option [value]=\"candidate.name\">{{ candidate.type.kind }}</option>\r\n }\r\n </datalist>\r\n <div>\r\n <button type=\"button\" class=\"dpe-btn\" (click)=\"addField()\">Aggiungi campo</button>\r\n </div>\r\n </section>\r\n </div>\r\n `,\r\n})\r\nexport class DpeDataSourceInspectorComponent extends DpeNodeInspectorBase<DataSourceNode> {\r\n private readonly metadata = inject(DpeMetadataStore);\r\n\r\n override readonly collection: DpeNodeCollectionName = 'dataSources';\r\n\r\n protected readonly kinds = KINDS;\r\n protected readonly delimiters = DELIMITERS;\r\n\r\n readonly tables = computed(() => this.metadata.tables());\r\n\r\n readonly fields = computed<readonly DataSourceField[]>(() => this.node().fields ?? []);\r\n\r\n /*\r\n * I tre campi condizionali **del nodo**, uno per voce di `DPE_DATA_SOURCE_CONDITIONS`, tutti\r\n * con la stessa forma: **la condizione vale oppure il campo porta un valore** (`frontend.md`\r\n * §5-ter). Nessuno dei tre e' diagnosticato fuori condizione — il backend ignora e basta — e\r\n * non cambia niente: la regola guarda il documento, non l'elenco dei codici.\r\n */\r\n readonly shows = dpeFieldVisibility(this.node, DPE_DATA_SOURCE_CONDITIONS);\r\n\r\n /**\r\n * Il tipo si dichiara solo dove non c'e' un catalogo da interrogare — **oppure** dove qualche\r\n * campo ne porta gia' uno: su una `Table` il tipo dichiarato il backend lo ignora, e una\r\n * colonna che nessuno mostra e' un valore che nessuno puo' togliere.\r\n *\r\n * Questa **non** e' una voce della tabella delle condizioni, ed e' l'unica del suo genere: li'\r\n * ogni riga e' una chiave del nodo, perche' la ripulitura toglie chiavi del nodo. Qui il campo\r\n * governato sta **dentro una riga di `fields`**, e la meta' «porta un valore» e' un `some(...)`\r\n * sulle righe invece della lettura di una chiave. La forma della regola resta quella, e il\r\n * predicato e' lo stesso (`declares`).\r\n */\r\n readonly showsTypes = computed(\r\n () =>\r\n this.node().kind !== 'Table' ||\r\n this.fields().some((field) => this.declares(field.declaredType)),\r\n );\r\n\r\n readonly providerFields = computed(() => {\r\n const tableName = this.node().tableName;\r\n if (!tableName) {\r\n return [];\r\n }\r\n return this.metadata.stateOf(tableName)?.table?.fields ?? [];\r\n });\r\n\r\n /** I tre esiti in una frase: sono situazioni diverse e vanno dette diverse. */\r\n readonly tableHint = computed(() => {\r\n const tableName = this.node().tableName;\r\n if (!tableName) {\r\n return 'Nome noto al provider di metadati dell’host, non al catalogo del database.';\r\n }\r\n const state = this.metadata.stateOf(tableName);\r\n if (!state) {\r\n return 'Lettura dei metadati in corso…';\r\n }\r\n switch (state.outcome) {\r\n case 'Declared':\r\n return `Tabella dichiarata: ${state.table?.fields?.length ?? 0} campi noti.`;\r\n case 'DeclaredWithoutFields':\r\n return 'Tabella dichiarata senza campi: i nomi si scrivono a mano, non c’e’ nulla da correggere.';\r\n case 'Unknown':\r\n return 'Tabella sconosciuta al provider: la validazione lo segnalera’.';\r\n default:\r\n return '';\r\n }\r\n });\r\n\r\n constructor() {\r\n super();\r\n this.metadata.loadTables();\r\n // Si chiede la descrizione della tabella riferita, una volta per nome. La cache e' dello\r\n // store: cambiare nodo e tornare indietro non rifa' la chiamata.\r\n effect(() => {\r\n const tableName = this.node().tableName;\r\n if (tableName) {\r\n this.metadata.describe(tableName);\r\n }\r\n });\r\n }\r\n\r\n protected addField(): void {\r\n this.patch({ fields: [...this.fields(), { name: '' }] });\r\n }\r\n\r\n protected removeField(index: number): void {\r\n this.patch({ fields: this.fields().filter((_, position) => position !== index) });\r\n }\r\n\r\n protected patchField(\r\n index: number,\r\n patch: Partial<DataSourceField> & { declaredType?: ColumnType },\r\n ): void {\r\n this.patch({\r\n fields: this.fields().map((field, position) =>\r\n position === index ? { ...field, ...patch } : field,\r\n ),\r\n });\r\n }\r\n\r\n protected patchCsv(patch: Partial<{ location: string; delimiter: string; hasHeaderRow: boolean }>): void {\r\n const current = this.node().csvOptions ?? { location: '' };\r\n this.patch({\r\n csvOptions: { ...current, ...(patch as Partial<typeof current>) },\r\n });\r\n }\r\n\r\n /*\r\n * Si toglie l'oggetto intero, non se ne svuotano i campi: un `csvOptions: {}` resta un\r\n * `csvOptions` dichiarato, e su una sorgente che non e' `Csv` e' esattamente il valore che\r\n * questa regola esiste per rendere togliibile.\r\n */\r\n protected removeCsvOptions(): void {\r\n this.patch({ csvOptions: undefined });\r\n }\r\n}\r\n","/**\r\n * Ispettore del filtro.\r\n *\r\n * Il vincolo condizionale del contratto governa cosa si vede: con `isDynamic` falso servono i\r\n * criteri e la `condition`; con `isDynamic` vero serve il nome del parametro di filtro, e i\r\n * criteri non si mostrano — **a meno che il documento non ne porti**, e allora si mostrano\r\n * (`frontend.md` §5-ter, normativa). Non e' un'attenuazione del vincolo: renderli non li rende\r\n * leciti, e a dire che non lo sono resta il backend con `DPE_MODEL_CONDITIONAL_FORBIDDEN`. E'\r\n * che un valore senza controllo non e' togliibile, e per **I7** sopravvive a ogni salvataggio.\r\n *\r\n * `condition` non e' SQL: e' una combinazione booleana dei numeri di sequenza dei criteri\r\n * (`1 AND (2 OR 3)`), ed e' l'unico linguaggio che il backend parsifica davvero. L'etichetta lo\r\n * dice, perche' una casella accanto a \"SQL nativo\" altrove invita a scriverci dentro SQL.\r\n */\r\n\r\nimport { ChangeDetectionStrategy, Component, computed } from '@angular/core';\r\n\r\nimport {\r\n DPE_FILTER_CONDITIONS,\r\n dpeFieldVisibility,\r\n} from '../../core/dpe-conditional-fields';\r\nimport {\r\n DpeNodeCollectionName,\r\n FilterCriterion,\r\n FilterNode,\r\n FilterOperator,\r\n} from '../../model/dpe-definition.model';\r\nimport { DpeSelectValueDirective } from '../shared/dpe-select-value.directive';\r\nimport { DpeNodeInspectorBase } from './dpe-node-inspector-base';\r\n\r\nconst OPERATORS: readonly FilterOperator[] = [\r\n 'Equals',\r\n 'NotEquals',\r\n 'GreaterThan',\r\n 'GreaterThanOrEqual',\r\n 'LessThan',\r\n 'LessThanOrEqual',\r\n 'StartsWith',\r\n 'EndsWith',\r\n 'Contains',\r\n 'DoesNotContain',\r\n 'IsNull',\r\n 'IsNotNull',\r\n 'In',\r\n 'NotIn',\r\n];\r\n\r\n/** Gli unici operatori che non prendono un valore: e' forma del controllo, non validazione. */\r\nconst UNARY: readonly FilterOperator[] = ['IsNull', 'IsNotNull'];\r\n\r\n@Component({\r\n selector: 'dpe-filter-inspector',\r\n standalone: true,\r\n imports: [DpeSelectValueDirective],\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n template: `\r\n <div class=\"dpe-form\">\r\n <label class=\"dpe-check\">\r\n <input\r\n type=\"checkbox\"\r\n [checked]=\"node().isDynamic ?? false\"\r\n (change)=\"setField('isDynamic', checkedOf($event))\"\r\n />\r\n Filtro dinamico (i criteri arrivano da un parametro)\r\n </label>\r\n\r\n <!--\r\n Le due meta’ del filtro non sono piu’ esclusive: per frontend.md §5-ter, normativa, **un\r\n campo che porta un valore si rende anche fuori dalla propria condizione**. Un documento\r\n scritto altrove puo’ portare i criteri **e** il parametro insieme, e con un ramo\r\n alternativo una delle due meta’ resterebbe invisibile e non cancellabile — il rilievo del\r\n backend (DPE_MODEL_CONDITIONAL_FORBIDDEN) arriverebbe su un campo senza controllo. Vuoto\r\n e non applicabile resta nascosto: nel caso normale si vede una meta’ sola, come prima.\r\n -->\r\n @if (shows.filterParameterName()) {\r\n <div class=\"dpe-field\">\r\n <label class=\"dpe-field__label\" for=\"filter-parameter\">Parametro di filtro</label>\r\n <select\r\n id=\"filter-parameter\"\r\n [dpeValue]=\"node().filterParameterName ?? ''\"\r\n (change)=\"setField('filterParameterName', valueOf($event))\"\r\n >\r\n <option value=\"\">— non impostato —</option>\r\n @for (parameter of filterParameters(); track $index) {\r\n <option [value]=\"parameter.name\">{{ parameter.label }} ({{ parameter.name }})</option>\r\n }\r\n </select>\r\n @if (filterParameters().length === 0) {\r\n <p class=\"dpe-field__hint\">\r\n Nessun parametro con ruolo <code>Filter</code> dichiarato sulla definizione.\r\n </p>\r\n }\r\n </div>\r\n }\r\n\r\n @if (shows.criteria()) {\r\n <section class=\"dpe-section\">\r\n <h4 class=\"dpe-section__title\">Criteri</h4>\r\n @if (criteria().length === 0) {\r\n <p class=\"dpe-empty\">Nessun criterio.</p>\r\n } @else {\r\n <table class=\"dpe-table\">\r\n <thead>\r\n <tr>\r\n <th>Seq.</th>\r\n <th>Colonna</th>\r\n <th>Operatore</th>\r\n <th>Valore</th>\r\n <th>Parametro</th>\r\n <th></th>\r\n </tr>\r\n </thead>\r\n <tbody>\r\n @for (criterion of criteria(); track $index) {\r\n <tr>\r\n <td>\r\n <input\r\n type=\"number\"\r\n min=\"1\"\r\n [value]=\"criterion.sequence\"\r\n [attr.aria-label]=\"'Sequenza del criterio ' + ($index + 1)\"\r\n (change)=\"patchCriterion($index, { sequence: number(valueOf($event)) })\"\r\n />\r\n </td>\r\n <td>\r\n <input\r\n type=\"text\"\r\n list=\"filter-columns\"\r\n [value]=\"criterion.sourceFieldName\"\r\n [attr.aria-label]=\"'Colonna del criterio ' + ($index + 1)\"\r\n (change)=\"patchCriterion($index, { sourceFieldName: valueOf($event) })\"\r\n />\r\n </td>\r\n <td>\r\n <select\r\n [dpeValue]=\"criterion.operator\"\r\n [attr.aria-label]=\"'Operatore del criterio ' + ($index + 1)\"\r\n (change)=\"patchCriterion($index, { operator: operator(valueOf($event)) })\"\r\n >\r\n @for (option of operators; track $index) {\r\n <option [value]=\"option\">{{ option }}</option>\r\n }\r\n </select>\r\n </td>\r\n <td>\r\n @if (!isUnary(criterion.operator)) {\r\n <input\r\n type=\"text\"\r\n [value]=\"criterion.value ?? ''\"\r\n [disabled]=\"!!criterion.inputVariable\"\r\n [attr.aria-label]=\"'Valore del criterio ' + ($index + 1)\"\r\n (change)=\"patchCriterion($index, { value: valueOf($event) || undefined })\"\r\n />\r\n }\r\n </td>\r\n <td>\r\n @if (!isUnary(criterion.operator)) {\r\n <!--\r\n Valore e parametro sono mutuamente esclusivi: sceglierne uno disabilita\r\n l’altro, cosi' lo stato \"entrambi\" non e’ rappresentabile.\r\n -->\r\n <select\r\n [dpeValue]=\"criterion.inputVariable ?? ''\"\r\n [attr.aria-label]=\"'Parametro del criterio ' + ($index + 1)\"\r\n (change)=\"onInputVariable($index, valueOf($event))\"\r\n >\r\n <option value=\"\">— nessuno —</option>\r\n @for (parameter of valueParameters(); track $index) {\r\n <option [value]=\"parameter.name\">{{ parameter.name }}</option>\r\n }\r\n </select>\r\n }\r\n </td>\r\n <td>\r\n <button\r\n type=\"button\"\r\n class=\"dpe-btn dpe-btn--icon dpe-btn--danger\"\r\n [attr.aria-label]=\"'Rimuovi il criterio ' + criterion.sequence\"\r\n (click)=\"removeCriterion($index)\"\r\n >\r\n ×\r\n </button>\r\n </td>\r\n </tr>\r\n }\r\n </tbody>\r\n </table>\r\n }\r\n <datalist id=\"filter-columns\">\r\n @for (column of inputColumns(); track $index) {\r\n <option [value]=\"column.name\">{{ column.type.kind }}</option>\r\n }\r\n </datalist>\r\n <div>\r\n <button type=\"button\" class=\"dpe-btn\" (click)=\"addCriterion()\">Aggiungi criterio</button>\r\n </div>\r\n </section>\r\n }\r\n\r\n @if (shows.condition()) {\r\n <div class=\"dpe-field\">\r\n <label class=\"dpe-field__label\" for=\"filter-condition\">Condizione sui numeri di sequenza</label>\r\n <input\r\n id=\"filter-condition\"\r\n type=\"text\"\r\n [value]=\"node().condition ?? ''\"\r\n placeholder=\"1 AND (2 OR 3)\"\r\n [attr.aria-invalid]=\"hasIssueOn('condition')\"\r\n (change)=\"setField('condition', valueOf($event))\"\r\n />\r\n <p class=\"dpe-field__hint\">\r\n Non e’ SQL: solo numeri di sequenza, <code>AND</code>, <code>OR</code>,\r\n <code>NOT</code> e parentesi.\r\n </p>\r\n @for (issue of issuesOn('condition'); track $index) {\r\n <p class=\"dpe-field__issue\">{{ issue.code }} — {{ issue.message }}</p>\r\n }\r\n </div>\r\n }\r\n </div>\r\n `,\r\n})\r\nexport class DpeFilterInspectorComponent extends DpeNodeInspectorBase<FilterNode> {\r\n override readonly collection: DpeNodeCollectionName = 'filters';\r\n\r\n protected readonly operators = OPERATORS;\r\n\r\n readonly criteria = computed<readonly FilterCriterion[]>(() => this.node().criteria ?? []);\r\n\r\n /*\r\n * I tre campi condizionali del filtro, uno per voce di `DPE_FILTER_CONDITIONS` e stessa forma:\r\n * **la condizione vale oppure il campo porta un valore** (`frontend.md` §5-ter). Un criterio si\r\n * toglie con il proprio ×, la condizione svuotando il testo, il parametro con la voce «— non\r\n * impostato —»: ognuno dei tre controlli sa gia' riportare il campo all'assenza, che e' la meta'\r\n * della regola senza la quale renderli non servirebbe a niente.\r\n */\r\n readonly shows = dpeFieldVisibility(this.node, DPE_FILTER_CONDITIONS);\r\n\r\n readonly filterParameters = computed(() =>\r\n this.parameters().filter((parameter) => parameter.role === 'Filter'),\r\n );\r\n\r\n readonly valueParameters = computed(() =>\r\n this.parameters().filter((parameter) => parameter.role === 'Value'),\r\n );\r\n\r\n protected isUnary(operator: FilterOperator): boolean {\r\n return UNARY.includes(operator);\r\n }\r\n\r\n protected operator(value: string): FilterOperator {\r\n return value as FilterOperator;\r\n }\r\n\r\n protected number(value: string): number {\r\n // Forma del campo: un numero di sequenza non numerico non e' rappresentabile, e 1 e' il\r\n // minimo dichiarato. Non e' validazione: l'unicita' la verifica il backend.\r\n const parsed = Number(value);\r\n return Number.isFinite(parsed) && parsed >= 1 ? Math.trunc(parsed) : 1;\r\n }\r\n\r\n protected addCriterion(): void {\r\n const nextSequence =\r\n this.criteria().reduce((max, criterion) => Math.max(max, criterion.sequence), 0) + 1;\r\n this.patch({\r\n criteria: [\r\n ...this.criteria(),\r\n { sequence: nextSequence, sourceFieldName: '', operator: 'Equals' },\r\n ],\r\n });\r\n }\r\n\r\n protected removeCriterion(index: number): void {\r\n this.patch({ criteria: this.criteria().filter((_, position) => position !== index) });\r\n }\r\n\r\n protected patchCriterion(index: number, patch: Partial<FilterCriterion>): void {\r\n this.patch({\r\n criteria: this.criteria().map((criterion, position) =>\r\n position === index ? { ...criterion, ...patch } : criterion,\r\n ),\r\n });\r\n }\r\n\r\n /** Scegliere un parametro **toglie** il valore letterale: sono esclusivi per contratto. */\r\n protected onInputVariable(index: number, name: string): void {\r\n this.patchCriterion(index, {\r\n inputVariable: name === '' ? undefined : name,\r\n value: name === '' ? this.criteria()[index]?.value : undefined,\r\n });\r\n }\r\n}\r\n","/**\r\n * Ispettore della previsione.\r\n *\r\n * Il nodo e' **modellato per intero e non compilabile in v1**: l'avviso sta in testa e non a\r\n * pie' di pagina, perche' e' la prima cosa da sapere prima di costruirci una pipeline intorno.\r\n * Non e' un errore dell'utente — e' `DPE_NODE_NOT_COMPILABLE`, l'unico codice che significa\r\n * \"hai fatto bene, siamo noi\" — quindi il colore non e' quello dell'errore.\r\n */\r\n\r\nimport { ChangeDetectionStrategy, Component, computed } from '@angular/core';\r\n\r\nimport {\r\n AggregateFunction,\r\n DatePart,\r\n DpeNodeCollectionName,\r\n ForecastAccuracy,\r\n ForecastAggregationField,\r\n ForecastGroupField,\r\n ForecastModelType,\r\n ForecastNode,\r\n ForecastPeriodType,\r\n} from '../../model/dpe-definition.model';\r\nimport { DpeSelectValueDirective } from '../shared/dpe-select-value.directive';\r\nimport { DpeNodeInspectorBase } from './dpe-node-inspector-base';\r\n\r\nconst PERIOD_TYPES: readonly ForecastPeriodType[] = [\r\n 'Year',\r\n 'YearQuarter',\r\n 'YearMonth',\r\n 'YearWeek',\r\n 'YearMonthDay',\r\n 'FiscalYear',\r\n 'FiscalYearQuarter',\r\n 'FiscalYearMonth',\r\n 'FiscalYearWeek',\r\n];\r\n\r\nconst MODELS: readonly ForecastModelType[] = ['Auto', 'Additive', 'Multiplicative'];\r\nconst ACCURACIES: readonly ForecastAccuracy[] = ['None', 'Eighty', 'NinetyFive'];\r\nconst FUNCTIONS: readonly AggregateFunction[] = ['Sum', 'Avg', 'Min', 'Max', 'Count'];\r\nconst DATE_PARTS: readonly DatePart[] = [\r\n 'Second',\r\n 'Minute',\r\n 'Hour',\r\n 'Day',\r\n 'Week',\r\n 'Month',\r\n 'Quarter',\r\n 'Year',\r\n];\r\n\r\n@Component({\r\n selector: 'dpe-forecast-inspector',\r\n standalone: true,\r\n imports: [DpeSelectValueDirective],\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n template: `\r\n <div class=\"dpe-form\">\r\n <p class=\"dpe-note dpe-note--not-compilable\">\r\n Nodo modellato per intero ma <strong>non traducibile in SQL</strong> nella versione 1:\r\n puoi descriverlo e salvarlo, la compilazione lo rifiuta con\r\n <code>DPE_NODE_NOT_COMPILABLE</code>. Non c’e’ niente da correggere.\r\n </p>\r\n\r\n <div class=\"dpe-row\">\r\n <div class=\"dpe-field\">\r\n <label class=\"dpe-field__label\" for=\"fc-date\">Campo data</label>\r\n <input\r\n id=\"fc-date\"\r\n type=\"text\"\r\n list=\"fc-columns\"\r\n [value]=\"node().dateFieldName ?? ''\"\r\n (change)=\"setField('dateFieldName', valueOf($event))\"\r\n />\r\n </div>\r\n <div class=\"dpe-field\">\r\n <label class=\"dpe-field__label\" for=\"fc-period\">Periodo</label>\r\n <select\r\n id=\"fc-period\"\r\n [dpeValue]=\"node().periodType ?? 'YearMonth'\"\r\n (change)=\"setField('periodType', valueOf($event))\"\r\n >\r\n @for (period of periodTypes; track $index) {\r\n <option [value]=\"period\">{{ period }}</option>\r\n }\r\n </select>\r\n </div>\r\n </div>\r\n\r\n <div class=\"dpe-row\">\r\n <div class=\"dpe-field\">\r\n <label class=\"dpe-field__label\" for=\"fc-count\">Numero di periodi</label>\r\n <input\r\n id=\"fc-count\"\r\n type=\"number\"\r\n min=\"1\"\r\n max=\"100\"\r\n [value]=\"node().periodCount ?? 1\"\r\n (change)=\"setField('periodCount', toNumber(valueOf($event)))\"\r\n />\r\n </div>\r\n <div class=\"dpe-field\">\r\n <label class=\"dpe-field__label\" for=\"fc-start\">Alias inizio periodo</label>\r\n <input\r\n id=\"fc-start\"\r\n type=\"text\"\r\n [value]=\"node().periodStartFieldName ?? ''\"\r\n (change)=\"setField('periodStartFieldName', valueOf($event))\"\r\n />\r\n </div>\r\n </div>\r\n\r\n <div class=\"dpe-row\">\r\n <div class=\"dpe-field\">\r\n <label class=\"dpe-field__label\" for=\"fc-model\">Modello</label>\r\n <select\r\n id=\"fc-model\"\r\n [dpeValue]=\"node().modelType ?? 'Auto'\"\r\n (change)=\"setField('modelType', valueOf($event))\"\r\n >\r\n @for (model of models; track $index) {\r\n <option [value]=\"model\">{{ model }}</option>\r\n }\r\n </select>\r\n </div>\r\n <div class=\"dpe-field\">\r\n <label class=\"dpe-field__label\" for=\"fc-seasonality\">Stagionalita’</label>\r\n <input\r\n id=\"fc-seasonality\"\r\n type=\"number\"\r\n min=\"2\"\r\n max=\"24\"\r\n [value]=\"node().seasonality ?? ''\"\r\n placeholder=\"automatica\"\r\n (change)=\"setField('seasonality', toNumber(valueOf($event)))\"\r\n />\r\n </div>\r\n <div class=\"dpe-field\">\r\n <label class=\"dpe-field__label\" for=\"fc-accuracy\">Intervallo di confidenza</label>\r\n <select\r\n id=\"fc-accuracy\"\r\n [dpeValue]=\"node().accuracyPercent ?? 'None'\"\r\n (change)=\"setField('accuracyPercent', valueOf($event))\"\r\n >\r\n @for (accuracy of accuracies; track $index) {\r\n <option [value]=\"accuracy\">{{ accuracy }}</option>\r\n }\r\n </select>\r\n </div>\r\n </div>\r\n\r\n <label class=\"dpe-check\">\r\n <input\r\n type=\"checkbox\"\r\n [checked]=\"node().excludesLastPeriod ?? false\"\r\n (change)=\"setField('excludesLastPeriod', checkedOf($event))\"\r\n />\r\n Escludi l’ultimo periodo (spesso incompleto)\r\n </label>\r\n\r\n <section class=\"dpe-section\">\r\n <h4 class=\"dpe-section__title\">Campi da prevedere</h4>\r\n @for (field of aggregationFields(); track $index) {\r\n <div class=\"dpe-row\">\r\n <input\r\n type=\"text\"\r\n list=\"fc-columns\"\r\n [value]=\"field.fieldName\"\r\n [attr.aria-label]=\"'Campo ' + ($index + 1)\"\r\n (change)=\"patchAggregation($index, { fieldName: valueOf($event) })\"\r\n />\r\n <select\r\n [dpeValue]=\"field.function\"\r\n [attr.aria-label]=\"'Funzione ' + ($index + 1)\"\r\n (change)=\"patchAggregation($index, { function: asFunction(valueOf($event)) })\"\r\n >\r\n @for (option of functions; track $index) {\r\n <option [value]=\"option\">{{ option }}</option>\r\n }\r\n </select>\r\n <input\r\n type=\"text\"\r\n [value]=\"field.resultAlias\"\r\n [attr.aria-label]=\"'Alias ' + ($index + 1)\"\r\n (change)=\"patchAggregation($index, { resultAlias: valueOf($event) })\"\r\n />\r\n <button\r\n type=\"button\"\r\n class=\"dpe-btn dpe-btn--icon dpe-btn--danger\"\r\n aria-label=\"Rimuovi il campo\"\r\n (click)=\"removeAggregation($index)\"\r\n >\r\n ×\r\n </button>\r\n </div>\r\n }\r\n <div>\r\n <button type=\"button\" class=\"dpe-btn\" (click)=\"addAggregation()\">Aggiungi campo</button>\r\n </div>\r\n </section>\r\n\r\n <section class=\"dpe-section\">\r\n <h4 class=\"dpe-section__title\">Raggruppamenti</h4>\r\n @for (group of groupFields(); track $index) {\r\n <div class=\"dpe-row\">\r\n <input\r\n type=\"text\"\r\n list=\"fc-columns\"\r\n [value]=\"group.fieldName\"\r\n [attr.aria-label]=\"'Campo di raggruppamento ' + ($index + 1)\"\r\n (change)=\"patchGroup($index, { fieldName: valueOf($event) })\"\r\n />\r\n <select\r\n [dpeValue]=\"group.datePart ?? ''\"\r\n [attr.aria-label]=\"'Parte di data ' + ($index + 1)\"\r\n (change)=\"patchGroup($index, { datePart: asDatePart(valueOf($event)) })\"\r\n >\r\n <option value=\"\">— nessuna —</option>\r\n @for (part of dateParts; track $index) {\r\n <option [value]=\"part\">{{ part }}</option>\r\n }\r\n </select>\r\n <button\r\n type=\"button\"\r\n class=\"dpe-btn dpe-btn--icon dpe-btn--danger\"\r\n aria-label=\"Rimuovi il raggruppamento\"\r\n (click)=\"removeGroup($index)\"\r\n >\r\n ×\r\n </button>\r\n </div>\r\n }\r\n <p class=\"dpe-field__hint\">\r\n La parte di data e’ richiesta quando il campo e’ di tipo <code>DateTime</code>.\r\n </p>\r\n <div>\r\n <button type=\"button\" class=\"dpe-btn\" (click)=\"addGroup()\">Aggiungi raggruppamento</button>\r\n </div>\r\n </section>\r\n\r\n <datalist id=\"fc-columns\">\r\n @for (column of inputColumns(); track $index) {\r\n <option [value]=\"column.name\">{{ column.type.kind }}</option>\r\n }\r\n </datalist>\r\n </div>\r\n `,\r\n})\r\nexport class DpeForecastInspectorComponent extends DpeNodeInspectorBase<ForecastNode> {\r\n override readonly collection: DpeNodeCollectionName = 'forecasts';\r\n\r\n protected readonly periodTypes = PERIOD_TYPES;\r\n protected readonly models = MODELS;\r\n protected readonly accuracies = ACCURACIES;\r\n protected readonly functions = FUNCTIONS;\r\n protected readonly dateParts = DATE_PARTS;\r\n\r\n readonly aggregationFields = computed<readonly ForecastAggregationField[]>(\r\n () => this.node().aggregationFields ?? [],\r\n );\r\n\r\n readonly groupFields = computed<readonly ForecastGroupField[]>(() => this.node().groupFields ?? []);\r\n\r\n protected asFunction(value: string): AggregateFunction {\r\n return value as AggregateFunction;\r\n }\r\n\r\n protected asDatePart(value: string): DatePart | undefined {\r\n return value === '' ? undefined : (value as DatePart);\r\n }\r\n\r\n protected addAggregation(): void {\r\n this.patch({\r\n aggregationFields: [\r\n ...this.aggregationFields(),\r\n { fieldName: '', function: 'Sum', resultAlias: '' },\r\n ],\r\n });\r\n }\r\n\r\n protected removeAggregation(index: number): void {\r\n this.patch({\r\n aggregationFields: this.aggregationFields().filter((_, position) => position !== index),\r\n });\r\n }\r\n\r\n protected patchAggregation(index: number, patch: Partial<ForecastAggregationField>): void {\r\n this.patch({\r\n aggregationFields: this.aggregationFields().map((field, position) =>\r\n position === index ? { ...field, ...patch } : field,\r\n ),\r\n });\r\n }\r\n\r\n protected addGroup(): void {\r\n this.patch({ groupFields: [...this.groupFields(), { fieldName: '' }] });\r\n }\r\n\r\n protected removeGroup(index: number): void {\r\n this.patch({ groupFields: this.groupFields().filter((_, position) => position !== index) });\r\n }\r\n\r\n protected patchGroup(index: number, patch: Partial<ForecastGroupField>): void {\r\n this.patch({\r\n groupFields: this.groupFields().map((group, position) =>\r\n position === index ? { ...group, ...patch } : group,\r\n ),\r\n });\r\n }\r\n}\r\n","/**\r\n * Ispettore del percorso gerarchico.\r\n *\r\n * E' il nodo che pone il vincolo di capacita' sul dialetto: richiede CTE ricorsive. Non lo\r\n * verifichiamo qui — `DPE_DIALECT_UNSUPPORTED` arriva dalla validazione — ma lo diciamo, perche'\r\n * un utente che non sa che questo nodo dipende dal dialetto non capirebbe la diagnostica.\r\n *\r\n * I campi di aggregazione compaiono solo con `isAggregationRequired`: e' il vincolo condizionale\r\n * del contratto, e senza il flag sono campi che il backend rifiuta.\r\n */\r\n\r\nimport { ChangeDetectionStrategy, Component, computed } from '@angular/core';\r\n\r\nimport {\r\n DPE_HIERARCHY_PATH_CONDITIONS,\r\n dpeFieldVisibility,\r\n} from '../../core/dpe-conditional-fields';\r\nimport {\r\n AggregateField,\r\n AggregateFunction,\r\n DpeNodeCollectionName,\r\n HierarchyPathNode,\r\n} from '../../model/dpe-definition.model';\r\nimport { DpeNodeInspectorBase } from './dpe-node-inspector-base';\r\n\r\nconst FUNCTIONS: readonly AggregateFunction[] = [\r\n 'Count',\r\n 'CountDistinct',\r\n 'Sum',\r\n 'Min',\r\n 'Max',\r\n 'Avg',\r\n];\r\n\r\n@Component({\r\n selector: 'dpe-hierarchy-path-inspector',\r\n standalone: true,\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n template: `\r\n <div class=\"dpe-form\">\r\n <p class=\"dpe-note\">\r\n Richiede le CTE ricorsive del dialetto di destinazione: se il dialetto scelto non le\r\n supporta, la validazione lo segnala come limite del <strong>dialetto</strong>, non della\r\n definizione.\r\n </p>\r\n\r\n <div class=\"dpe-row\">\r\n <div class=\"dpe-field\">\r\n <label class=\"dpe-field__label\" for=\"hp-self\">Campo chiave</label>\r\n <input\r\n id=\"hp-self\"\r\n type=\"text\"\r\n list=\"hp-columns\"\r\n [value]=\"node().selfFieldName ?? ''\"\r\n (change)=\"setField('selfFieldName', valueOf($event))\"\r\n />\r\n </div>\r\n <div class=\"dpe-field\">\r\n <label class=\"dpe-field__label\" for=\"hp-parent\">Campo padre</label>\r\n <input\r\n id=\"hp-parent\"\r\n type=\"text\"\r\n list=\"hp-columns\"\r\n [value]=\"node().parentFieldName ?? ''\"\r\n (change)=\"setField('parentFieldName', valueOf($event))\"\r\n />\r\n </div>\r\n </div>\r\n\r\n <div class=\"dpe-field\">\r\n <label class=\"dpe-field__label\" for=\"hp-path\">Alias della colonna di percorso</label>\r\n <input\r\n id=\"hp-path\"\r\n type=\"text\"\r\n [value]=\"node().pathFieldName ?? ''\"\r\n (change)=\"setField('pathFieldName', valueOf($event))\"\r\n />\r\n <p class=\"dpe-field__hint\">La produce il backend: e’ di tipo <code>String</code> e non nulla.</p>\r\n </div>\r\n\r\n <label class=\"dpe-check\">\r\n <input\r\n type=\"checkbox\"\r\n [checked]=\"node().includesSelfValue ?? true\"\r\n (change)=\"setField('includesSelfValue', checkedOf($event))\"\r\n />\r\n Includi il valore del nodo corrente nel percorso\r\n </label>\r\n\r\n <div class=\"dpe-field\">\r\n <label class=\"dpe-field__label\" for=\"hp-rollup\">Alias della percentuale di rollup</label>\r\n <input\r\n id=\"hp-rollup\"\r\n type=\"text\"\r\n [value]=\"node().rollupPercentageFieldName ?? ''\"\r\n (change)=\"setField('rollupPercentageFieldName', valueOf($event))\"\r\n />\r\n </div>\r\n\r\n <label class=\"dpe-check\">\r\n <input\r\n type=\"checkbox\"\r\n [checked]=\"node().isAggregationRequired ?? false\"\r\n (change)=\"setField('isAggregationRequired', checkedOf($event))\"\r\n />\r\n Aggrega lungo la gerarchia\r\n </label>\r\n\r\n <!--\r\n frontend.md §5-ter: i campi aggregati si rendono anche con l’aggregazione spenta, quando\r\n ce ne sono. Senza, chi ne dichiara e poi toglie la spunta si porta dietro una collezione\r\n che non vede piu’ nessuno e che nessuno puo’ svuotare — e per **I7** sopravvive a ogni\r\n salvataggio. Collezione vuota vale assente: spenta e senza campi, la sezione non compare.\r\n -->\r\n @if (shows.aggregateFields()) {\r\n <section class=\"dpe-section\">\r\n <h4 class=\"dpe-section__title\">Campi aggregati</h4>\r\n @if (fields().length === 0) {\r\n <p class=\"dpe-empty\">Nessun campo: con l’aggregazione attiva ne serve almeno uno.</p>\r\n } @else {\r\n <table class=\"dpe-table\">\r\n <thead>\r\n <tr>\r\n <th>Funzione</th>\r\n <th>Colonna</th>\r\n <th>Alias</th>\r\n <th></th>\r\n </tr>\r\n </thead>\r\n <tbody>\r\n @for (field of fields(); track $index) {\r\n <tr>\r\n <td>\r\n <select\r\n [value]=\"field.function\"\r\n [attr.aria-label]=\"'Funzione ' + ($index + 1)\"\r\n (change)=\"patchField($index, { function: asFunction(valueOf($event)) })\"\r\n >\r\n @for (option of functions; track $index) {\r\n <option [value]=\"option\">{{ option }}</option>\r\n }\r\n </select>\r\n </td>\r\n <td>\r\n <input\r\n type=\"text\"\r\n list=\"hp-columns\"\r\n [value]=\"field.sourceFieldName ?? ''\"\r\n [disabled]=\"field.function === 'Count'\"\r\n [attr.aria-label]=\"'Colonna ' + ($index + 1)\"\r\n (change)=\"patchField($index, { sourceFieldName: valueOf($event) || undefined })\"\r\n />\r\n </td>\r\n <td>\r\n <input\r\n type=\"text\"\r\n [value]=\"field.alias\"\r\n [attr.aria-label]=\"'Alias ' + ($index + 1)\"\r\n (change)=\"patchField($index, { alias: valueOf($event) })\"\r\n />\r\n </td>\r\n <td>\r\n <button\r\n type=\"button\"\r\n class=\"dpe-btn dpe-btn--icon dpe-btn--danger\"\r\n aria-label=\"Rimuovi il campo\"\r\n (click)=\"removeField($index)\"\r\n >\r\n ×\r\n </button>\r\n </td>\r\n </tr>\r\n }\r\n </tbody>\r\n </table>\r\n }\r\n <div>\r\n <button type=\"button\" class=\"dpe-btn\" (click)=\"addField()\">Aggiungi campo</button>\r\n </div>\r\n </section>\r\n }\r\n\r\n <datalist id=\"hp-columns\">\r\n @for (column of inputColumns(); track $index) {\r\n <option [value]=\"column.name\">{{ column.type.kind }}</option>\r\n }\r\n </datalist>\r\n </div>\r\n `,\r\n})\r\nexport class DpeHierarchyPathInspectorComponent extends DpeNodeInspectorBase<HierarchyPathNode> {\r\n override readonly collection: DpeNodeCollectionName = 'hierarchyPaths';\r\n\r\n protected readonly functions = FUNCTIONS;\r\n\r\n readonly fields = computed<readonly AggregateField[]>(() => this.node().aggregateFields ?? []);\r\n\r\n /** L'unico campo condizionale di qui, nella forma di `frontend.md` §5-ter. */\r\n readonly shows = dpeFieldVisibility(this.node, DPE_HIERARCHY_PATH_CONDITIONS);\r\n\r\n protected asFunction(value: string): AggregateFunction {\r\n return value as AggregateFunction;\r\n }\r\n\r\n protected addField(): void {\r\n this.patch({ aggregateFields: [...this.fields(), { function: 'Sum', alias: '' }] });\r\n }\r\n\r\n protected removeField(index: number): void {\r\n this.patch({ aggregateFields: this.fields().filter((_, position) => position !== index) });\r\n }\r\n\r\n protected patchField(index: number, patch: Partial<AggregateField>): void {\r\n this.patch({\r\n aggregateFields: this.fields().map((field, position) =>\r\n position === index ? { ...field, ...patch } : field,\r\n ),\r\n });\r\n }\r\n}\r\n","/**\r\n * Ispettore del join.\r\n *\r\n * Due cose che il canvas non puo' dire e che quindi stanno qui: quale sorgente e' la\r\n * **primaria** (il lato che sopravvive in un `LeftOuter`) e lo **schema di uscita esplicito** —\r\n * un join produce solo i campi dichiarati in `fields`, non l'unione dei due lati.\r\n *\r\n * `allowsMultipleLookupValues` compare solo con `kind === 'Lookup'`: altrove il contratto lo\r\n * dichiara non significativo, e un interruttore che non fa niente e' peggio di un interruttore\r\n * assente.\r\n */\r\n\r\nimport { ChangeDetectionStrategy, Component, computed } from '@angular/core';\r\n\r\nimport {\r\n DPE_JOIN_CONDITIONS,\r\n dpeFieldVisibility,\r\n} from '../../core/dpe-conditional-fields';\r\nimport {\r\n DpeNodeCollectionName,\r\n JoinKey,\r\n JoinKind,\r\n JoinNode,\r\n JoinResultField,\r\n} from '../../model/dpe-definition.model';\r\nimport { DpeSelectValueDirective } from '../shared/dpe-select-value.directive';\r\nimport { DpeNodeInspectorBase } from './dpe-node-inspector-base';\r\n\r\nconst KINDS: readonly JoinKind[] = ['Inner', 'LeftOuter', 'RightOuter', 'FullOuter', 'Lookup'];\r\n\r\n@Component({\r\n selector: 'dpe-join-inspector',\r\n standalone: true,\r\n imports: [DpeSelectValueDirective],\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n template: `\r\n <div class=\"dpe-form\">\r\n <div class=\"dpe-field\">\r\n <label class=\"dpe-field__label\" for=\"join-kind\">Tipo di join</label>\r\n <!--\r\n Il bordo del rilievo anche qui: e’ la tendina che porta il caso piu’ visibile di tutti —\r\n un FullOuter su SQLite — e senza aria-invalid sarebbe l’unico controllo diagnosticato\r\n senza il segno che l’utente ha imparato a cercare sui campi di testo.\r\n -->\r\n <select\r\n id=\"join-kind\"\r\n [dpeValue]=\"node().kind\"\r\n [attr.aria-invalid]=\"hasIssueOn('kind')\"\r\n (change)=\"setField('kind', valueOf($event))\"\r\n >\r\n @for (kind of kinds; track $index) {\r\n <option [value]=\"kind\">{{ kind }}</option>\r\n }\r\n </select>\r\n @for (issue of issuesOn('kind'); track $index) {\r\n <!--\r\n Un FullOuter su SQLite e’ DPE_DIALECT_UNSUPPORTED: il messaggio arriva da qui e non\r\n da un controllo locale, perche’ le capacita’ del dialetto le conosce il backend.\r\n -->\r\n <p class=\"dpe-field__issue\">{{ issue.code }} — {{ issue.message }}</p>\r\n }\r\n </div>\r\n\r\n <div class=\"dpe-row\">\r\n <div class=\"dpe-field\">\r\n <span class=\"dpe-field__label\">Sorgente primaria</span>\r\n <p class=\"dpe-field__hint\">{{ node().primarySourceName || 'non collegata' }}</p>\r\n </div>\r\n <div class=\"dpe-field\">\r\n <span class=\"dpe-field__label\">Sorgente secondaria</span>\r\n <p class=\"dpe-field__hint\">{{ node().secondarySourceName || 'non collegata' }}</p>\r\n </div>\r\n </div>\r\n <p class=\"dpe-field__hint\">Le sorgenti si collegano sul canvas, sulle due porte in alto.</p>\r\n\r\n <!--\r\n frontend.md §5-ter: la casella si rende anche fuori da Lookup quando il documento dichiara\r\n la spunta. Fuori da Lookup il backend non dice niente — il campo e’ semplicemente\r\n **ignorato** — ed e’ proprio per questo che deve restare visibile: nessuno lo segnala, e\r\n tornare a Lookup lo rimetterebbe in gioco. Spenta toglie la chiave, altrimenti il\r\n controllo non saprebbe dire «assente».\r\n -->\r\n @if (shows.allowsMultipleLookupValues()) {\r\n <label class=\"dpe-check\">\r\n <input\r\n type=\"checkbox\"\r\n [checked]=\"node().allowsMultipleLookupValues ?? false\"\r\n (change)=\"setFlag('allowsMultipleLookupValues', checkedOf($event))\"\r\n />\r\n Ammetti piu’ corrispondenze sul lato secondario\r\n </label>\r\n }\r\n\r\n <section class=\"dpe-section\">\r\n <h4 class=\"dpe-section__title\">Chiavi</h4>\r\n @if (keys().length === 0) {\r\n <p class=\"dpe-empty\">Nessuna chiave.</p>\r\n } @else {\r\n <table class=\"dpe-table\">\r\n <thead>\r\n <tr>\r\n <th>Campo primario</th>\r\n <th>Campo secondario</th>\r\n <th></th>\r\n </tr>\r\n </thead>\r\n <tbody>\r\n @for (key of keys(); track $index) {\r\n <tr>\r\n <td>\r\n <input\r\n type=\"text\"\r\n list=\"join-primary-columns\"\r\n [value]=\"key.primaryFieldName\"\r\n [attr.aria-label]=\"'Campo primario della chiave ' + ($index + 1)\"\r\n (change)=\"patchKey($index, { primaryFieldName: valueOf($event) })\"\r\n />\r\n </td>\r\n <td>\r\n <input\r\n type=\"text\"\r\n list=\"join-secondary-columns\"\r\n [value]=\"key.secondaryFieldName\"\r\n [attr.aria-label]=\"'Campo secondario della chiave ' + ($index + 1)\"\r\n (change)=\"patchKey($index, { secondaryFieldName: valueOf($event) })\"\r\n />\r\n </td>\r\n <td>\r\n <button\r\n type=\"button\"\r\n class=\"dpe-btn dpe-btn--icon dpe-btn--danger\"\r\n aria-label=\"Rimuovi la chiave\"\r\n (click)=\"removeKey($index)\"\r\n >\r\n ×\r\n </button>\r\n </td>\r\n </tr>\r\n }\r\n </tbody>\r\n </table>\r\n }\r\n <div>\r\n <button type=\"button\" class=\"dpe-btn\" (click)=\"addKey()\">Aggiungi chiave</button>\r\n </div>\r\n </section>\r\n\r\n <section class=\"dpe-section\">\r\n <h4 class=\"dpe-section__title\">Campi in uscita</h4>\r\n <p class=\"dpe-field__hint\">\r\n Lo schema del join e’ <strong>solo</strong> questo elenco: cio’ che non e’ dichiarato\r\n qui non arriva a valle.\r\n </p>\r\n @if (fields().length === 0) {\r\n <p class=\"dpe-empty\">Nessun campo dichiarato.</p>\r\n } @else {\r\n <table class=\"dpe-table\">\r\n <thead>\r\n <tr>\r\n <th>Da</th>\r\n <th>Campo</th>\r\n <th>Alias</th>\r\n <th></th>\r\n </tr>\r\n </thead>\r\n <tbody>\r\n @for (field of fields(); track $index) {\r\n <tr>\r\n <td>\r\n <!--\r\n Un elenco chiuso alle due sorgenti del nodo: il contratto dice che\r\n la sorgente del campo deve essere una delle due, e con una casella di testo\r\n digitarne una terza sarebbe possibile.\r\n -->\r\n <select\r\n [dpeValue]=\"field.sourceName\"\r\n [attr.aria-label]=\"'Sorgente del campo ' + ($index + 1)\"\r\n (change)=\"patchField($index, { sourceName: valueOf($event) })\"\r\n >\r\n <option value=\"\">—</option>\r\n @if (node().primarySourceName) {\r\n <option [value]=\"node().primarySourceName!\">\r\n {{ node().primarySourceName }} (primaria)\r\n </option>\r\n }\r\n @if (node().secondarySourceName) {\r\n <option [value]=\"node().secondarySourceName!\">\r\n {{ node().secondarySourceName }} (secondaria)\r\n </option>\r\n }\r\n </select>\r\n </td>\r\n <td>\r\n <input\r\n type=\"text\"\r\n [attr.list]=\"columnListIdFor(field)\"\r\n [value]=\"field.sourceFieldName\"\r\n [attr.aria-label]=\"'Campo ' + ($index + 1)\"\r\n (change)=\"patchField($index, { sourceFieldName: valueOf($event) })\"\r\n />\r\n </td>\r\n <td>\r\n <input\r\n type=\"text\"\r\n [value]=\"field.alias\"\r\n [attr.aria-label]=\"'Alias del campo ' + ($index + 1)\"\r\n (change)=\"patchField($index, { alias: valueOf($event) })\"\r\n />\r\n </td>\r\n <td>\r\n <button\r\n type=\"button\"\r\n class=\"dpe-btn dpe-btn--icon dpe-btn--danger\"\r\n aria-label=\"Rimuovi il campo\"\r\n (click)=\"removeField($index)\"\r\n >\r\n ×\r\n </button>\r\n </td>\r\n </tr>\r\n }\r\n </tbody>\r\n </table>\r\n }\r\n <div>\r\n <button type=\"button\" class=\"dpe-btn\" (click)=\"addField()\">Aggiungi campo</button>\r\n </div>\r\n </section>\r\n\r\n <datalist id=\"join-primary-columns\">\r\n @for (column of primaryColumns(); track $index) {\r\n <option [value]=\"column.name\">{{ column.type.kind }}</option>\r\n }\r\n </datalist>\r\n <datalist id=\"join-secondary-columns\">\r\n @for (column of secondaryColumns(); track $index) {\r\n <option [value]=\"column.name\">{{ column.type.kind }}</option>\r\n }\r\n </datalist>\r\n </div>\r\n `,\r\n})\r\nexport class DpeJoinInspectorComponent extends DpeNodeInspectorBase<JoinNode> {\r\n override readonly collection: DpeNodeCollectionName = 'joins';\r\n\r\n protected readonly kinds = KINDS;\r\n\r\n readonly keys = computed<readonly JoinKey[]>(() => this.node().keys ?? []);\r\n readonly fields = computed<readonly JoinResultField[]>(() => this.node().fields ?? []);\r\n\r\n /**\r\n * L'unico campo condizionale di questo nodo, nella forma di `frontend.md` §5-ter: **la\r\n * condizione vale oppure il campo porta un valore**. `false` vale assenza (`dpeDeclares`),\r\n * quindi una giunzione che non dichiara niente non guadagna una casella.\r\n */\r\n readonly shows = dpeFieldVisibility(this.node, DPE_JOIN_CONDITIONS);\r\n\r\n /** Gli schemi dei due lati, separati: qui `inputColumns` fusi non basterebbero. */\r\n readonly primaryColumns = computed(\r\n () => this.store.schemaByNode().get(this.node().primarySourceName ?? '')?.columns ?? [],\r\n );\r\n\r\n readonly secondaryColumns = computed(\r\n () => this.store.schemaByNode().get(this.node().secondarySourceName ?? '')?.columns ?? [],\r\n );\r\n\r\n /** L'elenco dei campi proposti segue il lato scelto sulla riga. */\r\n protected columnListIdFor(field: JoinResultField): string {\r\n return field.sourceName === this.node().secondarySourceName\r\n ? 'join-secondary-columns'\r\n : 'join-primary-columns';\r\n }\r\n\r\n protected addKey(): void {\r\n this.patch({ keys: [...this.keys(), { primaryFieldName: '', secondaryFieldName: '' }] });\r\n }\r\n\r\n protected removeKey(index: number): void {\r\n this.patch({ keys: this.keys().filter((_, position) => position !== index) });\r\n }\r\n\r\n protected patchKey(index: number, patch: Partial<JoinKey>): void {\r\n this.patch({\r\n keys: this.keys().map((key, position) => (position === index ? { ...key, ...patch } : key)),\r\n });\r\n }\r\n\r\n protected addField(): void {\r\n this.patch({\r\n fields: [\r\n ...this.fields(),\r\n { sourceName: this.node().primarySourceName ?? '', sourceFieldName: '', alias: '' },\r\n ],\r\n });\r\n }\r\n\r\n protected removeField(index: number): void {\r\n this.patch({ fields: this.fields().filter((_, position) => position !== index) });\r\n }\r\n\r\n protected patchField(index: number, patch: Partial<JoinResultField>): void {\r\n this.patch({\r\n fields: this.fields().map((field, position) =>\r\n position === index ? { ...field, ...patch } : field,\r\n ),\r\n });\r\n }\r\n}\r\n","/**\r\n * L'editor di un'espressione.\r\n *\r\n * **Le espressioni non sono piu' SQL: sono formule** (ADR 0031), e le compila un componente\r\n * registrato dall'ospite. L'editor non conosce il linguaggio, non lo valida e non lo compila:\r\n * mostra, manda, e rende cio' che torna.\r\n *\r\n * Da qui la scomparsa dell'avviso di sicurezza che stava accanto al campo. Diceva che scrivere\r\n * un'espressione equivale a eseguire SQL arbitrario sul database, ed era vero sotto l'ADR 0006:\r\n * con le formule non lo e' piu', e cio' che raggiunge il database e' cio' che il compilatore\r\n * emette. Lasciarlo sarebbe dire il falso nel punto esatto in cui l'utente lo legge — il difetto\r\n * peggiore che un avviso possa avere.\r\n *\r\n * Al suo posto tre cose, e nessuna e' una regola di dominio:\r\n *\r\n * 1. **l'etichetta nomina il linguaggio**, non il dialetto: il dialetto non c'entra piu' con cio'\r\n * che si scrive qui. Il nome arriva da `DpeEnvironment.expressionLanguage` e **non** dal\r\n * catalogo, perche' il catalogo puo' non arrivare (compilatore che non lo fornisce, dialetto\r\n * non coperto) proprio quando serve sapere in che lingua si sta scrivendo;\r\n * 2. **le diagnostiche del backend accanto al campo**, con il loro codice e il loro messaggio. E'\r\n * la ragione per cui esiste un compilatore di formule: sa spiegare un errore. Il messaggio\r\n * porta il testo dell'ospite verbatim e la posizione quando c'e' («dal carattere 4 per 2\r\n * caratteri: Funzione 'SOMMA' sconosciuta: forse intendevi 'SUM'?»), e l'editor **non lo\r\n * ricompone** — lo rende;\r\n * 3. **cosa il linguaggio mette a disposizione in questo nodo**, come conteggio. Un conteggio e\r\n * non un elenco: la palette delle funzioni con firma e descrizione, l'elenco dei simboli e\r\n * l'inserimento al cursore sono un lotto a parte (ADR 0036 §2), e non bloccano niente.\r\n *\r\n * I **tre esiti** del catalogo si rendono diversi perche' sono diversi (`contracts.md` §7):\r\n * tavolozza, fallimento con la riga in testa che nomina la sorgente, catalogo consegnato vuoto\r\n * senza nessun rumore. Prima ne arrivavano due — un catalogo oppure un errore di trasporto — e\r\n * *dialetto non coperto* e *backend rotto* si vedevano identici, cioe' l'editor degradava in\r\n * silenzio. La distinzione la fa `dpeFormulaCatalogOutcome`, non un `if` qui dentro.\r\n *\r\n * La **forma pretesa** dal campo si mostra accanto all'etichetta e arriva dal backend. Non si\r\n * deduce dal tipo di nodo: sarebbe una regola di dominio in TypeScript, che e' il confine su cui\r\n * questo componente e' costruito.\r\n *\r\n * Arrivando dal backend, la forma ha una **finestra di ritardo**: fra il gesto che la cambia e la\r\n * risposta della validazione la pastiglia mostra quella precedente. Per questo, mentre una\r\n * validazione e' in volo, si rende **«in aggiornamento» e non asserita** (ADR 0042, `isFormUpdating`).\r\n * Il **blocco** per forma non supportata invece **resta applicato** — si ammorbidisce\r\n * l'affermazione, non la protezione: rilasciarlo inviterebbe a scrivere in un campo che sta per\r\n * essere bloccato, e l'input verrebbe congelato a meta' parola.\r\n *\r\n * E quando quella forma **non e'** fra quelle che il compilatore registrato sa produrre\r\n * (`DpeEnvironment.supportedForms`), il campo **non si scrive e dice perche'**. Le due meta'\r\n * arrivano da due posti — la forma dallo **schema** del nodo, le capacita' dall'ambiente — e\r\n * l'editor le incrocia in `dpeExpressionFormSupport`, funzione pura e non un `if` qui dentro.\r\n * Fino a quando il secondo operando non stava sul filo, questo campo sapeva dire cosa serve ma\r\n * non che non si potra' avere: l'utente scriveva una formula destinata a non compilare mai.\r\n *\r\n * **Capacita' non dichiarate non disabilitano niente**: non sapere non autorizza a indovinare.\r\n *\r\n * **Nessuna validazione di formula in TypeScript, per nessun motivo** — nemmeno «solo le\r\n * parentesi». La sola autorita' sul linguaggio e' il compilatore dell'ospite; un controllo locale\r\n * sarebbe una regola di dominio nel posto sbagliato, piu' una che diverge il giorno in cui il\r\n * linguaggio cambia.\r\n */\r\n\r\nimport { ChangeDetectionStrategy, Component, computed, input, output } from '@angular/core';\r\n\r\nimport { DpeExpressionForm } from '../../model/dpe-api.model';\r\nimport {\r\n DpeExpressionFormSupport,\r\n DpeFormulaCatalogOutcome,\r\n dpeExpressionFormPrompt,\r\n dpeFormulaCatalogSize,\r\n} from '../../core/dpe-formula-catalog.view';\r\nimport { Diagnostic } from '../../model/dpe-diagnostics.model';\r\n\r\n@Component({\r\n selector: 'dpe-expression-editor',\r\n standalone: true,\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n template: `\r\n <div class=\"dpe-field\">\r\n <!--\r\n Due pastiglie e non una: il **linguaggio** dice in che lingua si scrive, la **forma** dice\r\n che cosa il compilatore si aspetta qui. La seconda arriva dal backend\r\n (DpeNodeSchemaInfo.requiredForm, cioe’ con la validazione) e non si deduce dal tipo di nodo\r\n — dedurla sarebbe una regola di dominio in TypeScript.\r\n -->\r\n <label class=\"dpe-field__label\" [attr.for]=\"controlId()\">\r\n {{ label() }}\r\n @if (language(); as name) {\r\n <span class=\"dpe-expr__language\">{{ name }}</span>\r\n }\r\n <!--\r\n **Mentre la validazione e’ in volo la pastiglia non si afferma** (ADR 0042). La forma\r\n arriva da validate, quindi fra il gesto che la cambia — il tipo di una trasformazione —\r\n e la risposta c’e’ una finestra in cui questa pastiglia mostra la forma **precedente**\r\n con la faccia di un fatto. Misurata a 2,58 s dentro la pagina, ed esiste per costruzione:\r\n su un ospite vero il numero cambia, la finestra no.\r\n\r\n Non si spegne e non sparisce: si segna «in aggiornamento», cioe’ resta leggibile e smette\r\n di asserire. L’attributo aria-busy lo dice a chi non vede il colore.\r\n -->\r\n @if (formPrompt(); as prompt) {\r\n <span [class]=\"formClass()\" [title]=\"formTitle()\" [attr.aria-busy]=\"ariaBusy()\"\r\n >{{ prompt }}@if (isFormUpdating()) {\r\n <span class=\"dpe-expr__form-note\"> · in aggiornamento</span>\r\n }</span\r\n >\r\n }\r\n </label>\r\n <!--\r\n **La forma richiesta non e’ fra quelle che il compilatore sa produrre**: il campo non si\r\n scrive, e il perche’ sta sotto.\r\n\r\n Non «disabled» ma «readonly» piu’ «aria-disabled», ed e’ una scelta: un controllo\r\n disabilitato esce dall’ordine di tabulazione e il suo testo non si seleziona, cioe’ chi\r\n naviga da tastiera non raggiunge il campo e chi ha gia’ scritto una formula non puo’ piu’\r\n copiarla — proprio nel caso in cui gli servirebbe, perche’ quella formula va portata\r\n altrove. L’effetto voluto e’ «non si scrive», e la sola lettura lo ottiene per intero.\r\n -->\r\n <textarea\r\n [id]=\"controlId()\"\r\n [class]=\"inputClass()\"\r\n [value]=\"value() ?? ''\"\r\n [readOnly]=\"isBlocked()\"\r\n [attr.aria-disabled]=\"isBlocked() ? 'true' : null\"\r\n [attr.placeholder]=\"placeholder()\"\r\n [attr.aria-describedby]=\"describedBy()\"\r\n [attr.aria-invalid]=\"hasError() ? 'true' : null\"\r\n spellcheck=\"false\"\r\n (change)=\"valueChange.emit(text($event))\"\r\n ></textarea>\r\n\r\n @if (blockedReason(); as reason) {\r\n <p class=\"dpe-note dpe-note--not-compilable dpe-expr__blocked\" [id]=\"controlId() + '-blocked'\">\r\n {{ reason }}\r\n </p>\r\n }\r\n\r\n <!--\r\n Il rilievo del compilatore, **verbatim**. Il track per indice non e’ pigrizia: le chiavi\r\n composte da codice e percorso si duplicano davvero su due campi della stessa riga, e\r\n Angular tratta la chiave doppia come errore (NG0955).\r\n\r\n Nessuna classe statica accanto al binding di class: il binding a stringa governa l’intera\r\n lista, e una classe scritta anche nell’attributo statico sembra ridondante finche’ non\r\n sparisce.\r\n -->\r\n <div [id]=\"controlId() + '-issues'\">\r\n @for (issue of issues(); track $index) {\r\n <p [class]=\"issueClass(issue)\">{{ issue.code }} — {{ issue.message }}</p>\r\n }\r\n </div>\r\n\r\n <!--\r\n **Il catalogo non e' stato prodotto, e il perche' si vede.** Non e' un rilievo sulla\r\n formula: e’ un rilievo sul servizio che descrive il linguaggio, quindi sta in un blocco\r\n suo e non fra i rilievi del compilatore, dove l’utente lo leggerebbe come «ho sbagliato a\r\n scrivere».\r\n\r\n La riga in testa e’ DPE_CATALOG_UNAVAILABLE quando c’e’: nomina la sorgente su cui agire.\r\n Le strutturali che seguono ne dicono la causa e stanno in un dettaglio richiudibile — non\r\n si filtrano per pertinenza (l’errore che spiega lo schema puo’ stare a piu’ salti di\r\n distanza), quindi si dichiarano come dettaglio invece di diventare rumore.\r\n -->\r\n @if (failure(); as outcome) {\r\n <div [class]=\"failureClass()\" [id]=\"controlId() + '-catalog'\">\r\n <p class=\"dpe-expr__catalog-lead\">\r\n {{ outcome.lead.code }} — {{ outcome.lead.message }}\r\n </p>\r\n @if (outcome.details.length > 0) {\r\n <details class=\"dpe-expr__catalog-details\">\r\n <summary>\r\n Dettaglio: {{ outcome.details.length }} rilievi strutturali della definizione\r\n </summary>\r\n @for (detail of outcome.details; track $index) {\r\n <p [class]=\"issueClass(detail)\">{{ detail.code }} — {{ detail.message }}</p>\r\n }\r\n </details>\r\n }\r\n </div>\r\n }\r\n\r\n <p class=\"dpe-field__hint\" [id]=\"controlId() + '-hint'\">\r\n @if (columnHint().length > 0) {\r\n Colonne in ingresso: {{ columnHint() }}\r\n } @else {\r\n Le colonne in ingresso arrivano dalla validazione.\r\n }\r\n <!--\r\n Il conteggio compare **solo** con una tavolozza vera. Il terzo esito — catalogo\r\n consegnato vuoto perche’ il compilatore non descrive il proprio linguaggio — non e’ un\r\n guasto e non merita nessuna riga: niente tavolozza e **nessun rumore**.\r\n -->\r\n @if (isCatalogLoading()) {\r\n · <span class=\"dpe-expr__catalog\">lettura del catalogo…</span>\r\n } @else if (catalogHint(); as hint) {\r\n · <span class=\"dpe-expr__catalog\">{{ hint }}</span>\r\n }\r\n </p>\r\n </div>\r\n `,\r\n})\r\nexport class DpeExpressionEditorComponent {\r\n readonly label = input('Espressione');\r\n readonly value = input<string | undefined>(undefined);\r\n readonly placeholder = input('');\r\n /**\r\n * Il nome del linguaggio di formule dichiarato dall'ambiente. Assente: l'etichetta non nomina\r\n * niente. **Non** ripiega sul dialetto — sarebbe la bugia che questo lotto e' venuto a togliere.\r\n */\r\n readonly language = input<string | undefined>(undefined);\r\n /**\r\n * I nomi delle colonne a monte, **come suggerimento**: sono cio' che il backend ha propagato\r\n * (**I4**), e l'editor non li calcola.\r\n */\r\n readonly columns = input<readonly string[]>([]);\r\n /**\r\n * Le diagnostiche del backend **su questo campo**, gia' filtrate da chi conosce il percorso.\r\n * Si rendono e basta: il messaggio lo compone il backend, che e' l'unico posto in cui un\r\n * messaggio viene composto.\r\n */\r\n readonly issues = input<readonly Diagnostic[]>([]);\r\n /**\r\n * **Quale dei tre esiti del catalogo** si sta guardando, gia' letto da `dpeFormulaCatalogOutcome`.\r\n *\r\n * Prende l'esito e non il catalogo perche' il catalogo da solo non basta piu' a distinguerli: un\r\n * compilatore che non descrive il proprio linguaggio consegna il catalogo **presente e vuoto**,\r\n * quindi «assente» e «vuoto» sono due situazioni diverse che un solo oggetto nullabile\r\n * appiattiva — ed e' l'appiattimento che faceva sembrare uguali un dialetto non coperto e un\r\n * backend rotto.\r\n */\r\n readonly outcome = input<DpeFormulaCatalogOutcome>({ kind: 'Unknown' });\r\n /**\r\n * La forma pretesa dal campo, **dal backend**. Assente: il campo non la nomina. Non si deduce\r\n * dal tipo di nodo, che sarebbe una regola di dominio scritta dalla parte sbagliata del confine.\r\n */\r\n readonly requiredForm = input<DpeExpressionForm | undefined>(undefined);\r\n /**\r\n * L'incrocio **gia' fatto** fra la forma richiesta da questo nodo e le forme che il compilatore\r\n * registrato sa produrre (`DpeEnvironment.supportedForms`), come per `outcome`: il componente\r\n * riceve l'esito, non i due operandi.\r\n *\r\n * Il difetto predefinito e' «supportato», ed e' la politica del contratto: **capacita' non\r\n * dichiarate non disabilitano niente**. Un ospite che monta questo componente senza passare\r\n * nulla ottiene un campo che si scrive, non uno spento per ignoranza.\r\n */\r\n readonly formSupport = input<DpeExpressionFormSupport>({ isSupported: true });\r\n /**\r\n * **Una validazione e' in volo**, quindi la forma qui sopra puo' non essere ancora quella del\r\n * documento che l'utente ha davanti (ADR 0042). La pastiglia si segna «in aggiornamento» e\r\n * smette di asserire; il **blocco resta applicato**.\r\n *\r\n * E' di **documento** e **transitorio**, e per questo non e' una regola di dominio: arriva da\r\n * `DpeValidationService.isValidating`, che esisteva gia'. L'editor non sa — e non deve sapere —\r\n * che la forma dipende dal tipo del nodo: saperlo sarebbe riscrivere in TypeScript la regola che\r\n * `requiredForm` attraversa il confine per non avere.\r\n *\r\n * Non e' «il documento e' cambiato dall'ultima validazione», che e' il rimedio che l'ADR 0042\r\n * rifiuta: quello sarebbe vero **per tutta** la digitazione e spegnerebbe la pastiglia proprio\r\n * mentre si scrive nel campo che accompagna. `isValidating` diventa vero solo dopo la pausa di\r\n * digitazione, quindi mentre si scrive questa pastiglia resta ferma — e corretta, perche' la\r\n * forma non e' cambiata.\r\n *\r\n * Difetto predefinito `false`: un ospite che monta il componente senza passare nulla ottiene una\r\n * pastiglia asserita, che e' cio' che era prima.\r\n */\r\n readonly isFormUpdating = input(false);\r\n readonly isCatalogLoading = input(false);\r\n readonly controlId = input('dpe-expression');\r\n\r\n readonly valueChange = output<string>();\r\n\r\n readonly hasError = computed(() => this.issues().some((issue) => issue.severity === 'Error'));\r\n\r\n /** Elenco troncato: dieci nomi sono un aiuto, cinquanta sono un muro di testo. */\r\n readonly columnHint = computed(() => {\r\n const columns = this.columns();\r\n const shown = columns.slice(0, 10).join(', ');\r\n return columns.length > 10 ? `${shown}, …` : shown;\r\n });\r\n\r\n /**\r\n * La forma pretesa, detta all'utente. Assente quando il nodo non ospita formule: il campo non\r\n * nomina nessuna forma di ripiego, che sarebbe la deduzione vietata travestita da default.\r\n */\r\n readonly formPrompt = computed(() => dpeExpressionFormPrompt(this.requiredForm()));\r\n\r\n /**\r\n * La classe della pastiglia. Binding a stringa e nessuna classe statica accanto, come per il\r\n * controllo bloccato: il binding governa l'intera lista.\r\n *\r\n * Il modificatore non cambia il testo, cambia il **tono**: la pastiglia perde la tinta d'accento\r\n * — che e' cio' che la faceva leggere come un fatto corrente — e prende il bordo tratteggiato\r\n * della cosa provvisoria.\r\n */\r\n readonly formClass = computed(() =>\r\n this.isFormUpdating() ? 'dpe-expr__form dpe-expr__form--updating' : 'dpe-expr__form',\r\n );\r\n\r\n /**\r\n * Il titolo dice **cosa aspettarsi**, e nella finestra di aggiornamento dice anche che il blocco\r\n * non si tocca: e' la meta' che l'utente non puo' dedurre dal colore, e senza la quale un campo\r\n * che non accetta testo accanto a un'etichetta incerta sembra rotto.\r\n */\r\n readonly formTitle = computed(() =>\r\n this.isFormUpdating()\r\n ? 'Una validazione e’ in corso: questa forma potrebbe non essere ancora aggiornata. ' +\r\n 'Il blocco del campo resta applicato finche’ la risposta non arriva.'\r\n : 'La forma che il compilatore si aspetta in questo campo, dichiarata dal backend.',\r\n );\r\n\r\n /** `aria-busy` invece del solo colore: chi usa un lettore di schermo deve sentirlo. */\r\n readonly ariaBusy = computed(() => (this.isFormUpdating() ? 'true' : null));\r\n\r\n /** Il campo non si scrive: la forma richiesta qui non e' fra quelle che il compilatore produce. */\r\n readonly isBlocked = computed(() => !this.formSupport().isSupported);\r\n\r\n /**\r\n * Il motivo, **presente solo quando il campo e' bloccato**. Un campo inerte e muto fa cercare\r\n * all'utente un errore nella propria formula, che e' l'unico posto in cui il problema non sta.\r\n */\r\n readonly blockedReason = computed(() =>\r\n this.formSupport().isSupported ? undefined : this.formSupport().reason,\r\n );\r\n\r\n /**\r\n * La classe del controllo bloccato. Binding a stringa e nessuna classe statica accanto, come per\r\n * i rilievi: il binding governa l'intera lista e una classe scritta anche nell'attributo statico\r\n * sembra ridondante finche' non sparisce.\r\n */\r\n readonly inputClass = computed(() => (this.isBlocked() ? 'dpe-expr__input--blocked' : ''));\r\n\r\n /** L'esito di fallimento, quando e' quello: il template rende solo se c'e'. */\r\n readonly failure = computed(() => {\r\n const outcome = this.outcome();\r\n return outcome.kind === 'Failed' ? outcome : undefined;\r\n });\r\n\r\n /**\r\n * Il blocco del fallimento prende il bordo dell'errore quando fra le diagnostiche c'e' un\r\n * `Error`, quello dell'avviso altrimenti. Non e' decorazione: «il catalogo non c'e' perche' la\r\n * definizione e' rotta a monte» e «il catalogo non c'e' e basta» si correggono in modi diversi.\r\n */\r\n readonly failureClass = computed(() =>\r\n this.failure()?.hasError ? 'dpe-note dpe-note--danger' : 'dpe-note dpe-note--warning',\r\n );\r\n\r\n /**\r\n * Cosa il linguaggio offre **qui**, in numeri. E' deliberatamente un conteggio: dice che il\r\n * catalogo e' arrivato e che dipende dal nodo (i simboli cambiano con lo schema a monte), senza\r\n * anticipare l'elenco navigabile, che e' un componente e va progettato guardandolo.\r\n *\r\n * Solo per l'esito `Palette`: un conteggio di zeri sotto un campo direbbe che qualcosa e'\r\n * andato storto, mentre il compilatore ha soltanto scelto di non descriversi.\r\n */\r\n readonly catalogHint = computed<string | undefined>(() => {\r\n const outcome = this.outcome();\r\n if (outcome.kind !== 'Palette') {\r\n return undefined;\r\n }\r\n const catalog = outcome.catalog;\r\n return (\r\n `${catalog.functions.length} funzioni, ${catalog.operators.length} operatori, ` +\r\n `${catalog.symbols.length} simboli disponibili (${dpeFormulaCatalogSize(catalog)} voci)`\r\n );\r\n });\r\n\r\n /**\r\n * `aria-describedby` porta **il suggerimento, i rilievi e il motivo per cui la tavolozza manca**:\r\n * un lettore di schermo deve sentire l'errore del compilatore leggendo il campo, non cercandolo\r\n * piu' in basso, e deve sapere che qui non c'e' aiuto disponibile prima di scrivere.\r\n */\r\n readonly describedBy = computed(() => {\r\n const id = this.controlId();\r\n const parts: string[] = [];\r\n // Per primo il motivo del blocco quando c'e': e' l'unica cosa che cambia cosa si puo' fare\r\n // qui, e sentirlo dopo tre rilievi significa averli ascoltati per niente.\r\n if (this.isBlocked()) {\r\n parts.push(`${id}-blocked`);\r\n }\r\n if (this.issues().length > 0) {\r\n parts.push(`${id}-issues`);\r\n }\r\n if (this.failure()) {\r\n parts.push(`${id}-catalog`);\r\n }\r\n parts.push(`${id}-hint`);\r\n return parts.join(' ');\r\n });\r\n\r\n protected issueClass(issue: Diagnostic): string {\r\n if (issue.severity === 'Warning') {\r\n return 'dpe-field__issue dpe-field__issue--warning';\r\n }\r\n if (issue.severity === 'Info') {\r\n return 'dpe-field__issue dpe-field__issue--info';\r\n }\r\n return 'dpe-field__issue';\r\n }\r\n\r\n protected text(event: Event): string {\r\n return (event.target as HTMLTextAreaElement).value;\r\n }\r\n}\r\n","/**\r\n * Ispettore della trasformazione.\r\n *\r\n * `kind` governa i campi, e la corrispondenza e' quella del contratto: `expressionFields` per\r\n * `Expression` e `ComputeRelative`, `droppedFields` per `Slice`, `partitionBy`/`orderBy` solo\r\n * per `ComputeRelative`. Il vincolo e' **esclusivo** in entrambi i sensi: per `Slice` le\r\n * espressioni devono essere vuote.\r\n *\r\n * Quelle condizioni non sono scritte qui: stanno in `DPE_TRANSFORM_CONDITIONS`\r\n * (`core/dpe-conditional-fields.ts`), **un punto solo** letto sia dalla resa sia dalla ripulitura.\r\n * Prima erano due copie a pochi metodi di distanza in questo stesso file, e una divergenza fra le\r\n * due non l'avrebbe vista nessun test.\r\n *\r\n * Cambiare `kind` **ripulisce** i campi che il tipo nuovo non governa, ed e' la stessa decisione\r\n * del cambio di destinazione su un writeback (`frontend.md` §5-ter, ultima parte): scegliere il\r\n * tipo e' un gesto deliberato con cui l'autore dichiara inapplicabili i campi del tipo precedente,\r\n * e ripulire esegue quell'intento invece di lasciargli un rilievo per campo da spegnere a mano.\r\n * Due comportamenti diversi per la stessa situazione su due tipi di nodo diventerebbero folklore,\r\n * e il folklore non si documenta.\r\n *\r\n * Ripulire e' **togliere la chiave**, non scriverci un valore vuoto, come fa `setFlag()`: un\r\n * `droppedFields: []` sarebbe una collezione a nome di nessuno nel documento (**I7**).\r\n *\r\n * La ripulitura non rende inutile l'altra meta', che e' normativa (`frontend.md` §5-ter): **un\r\n * campo che porta un valore si rende anche fuori dalla propria condizione**. Le due regole si\r\n * dividono il lavoro — questa copre le modifiche fatte qui, quella copre i documenti scritti\r\n * altrove, che sono l'unico modo rimasto di far arrivare un valore fuori condizione.\r\n */\r\n\r\nimport { ChangeDetectionStrategy, Component, computed, effect, inject } from '@angular/core';\r\n\r\nimport {\r\n DPE_TRANSFORM_CONDITIONS,\r\n dpeFieldVisibility,\r\n} from '../../core/dpe-conditional-fields';\r\nimport { DpeFormulaCatalogStore } from '../../core/dpe-formula-catalog.store';\r\nimport { dpeExpressionFormSupport } from '../../core/dpe-formula-catalog.view';\r\nimport { DpeValidationService } from '../../core/dpe-validation.service';\r\nimport {\r\n DpeNodeCollectionName,\r\n ExpressionField,\r\n OrderByField,\r\n TransformKind,\r\n TransformNode,\r\n} from '../../model/dpe-definition.model';\r\nimport { DpeColumnTypeEditorComponent } from '../shared/dpe-column-type-editor.component';\r\nimport { DpeExpressionEditorComponent } from '../shared/dpe-expression-editor.component';\r\nimport { DpeSelectValueDirective } from '../shared/dpe-select-value.directive';\r\nimport { DpeNodeInspectorBase } from './dpe-node-inspector-base';\r\n\r\nconst KINDS: readonly TransformKind[] = ['Expression', 'Slice', 'ComputeRelative'];\r\n\r\n@Component({\r\n selector: 'dpe-transform-inspector',\r\n standalone: true,\r\n imports: [DpeColumnTypeEditorComponent, DpeExpressionEditorComponent, DpeSelectValueDirective],\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n template: `\r\n <div class=\"dpe-form\">\r\n <div class=\"dpe-field\">\r\n <label class=\"dpe-field__label\" for=\"tr-kind\">Tipo di trasformazione</label>\r\n <select id=\"tr-kind\" [dpeValue]=\"node().kind\" (change)=\"onKind(valueOf($event))\">\r\n @for (kind of kinds; track $index) {\r\n <option [value]=\"kind\">{{ kind }}</option>\r\n }\r\n </select>\r\n <!--\r\n L’avviso dice cosa succede **prima** che succeda, perche’ e’ una perdita di campi su un\r\n gesto di routine. Non nomina il ↶ della barra: con la finestra aperta quel bottone e’\r\n irraggiungibile, e una frase vera solo dopo la chiusura e’ il difetto appena corretto nel\r\n piede della dialog.\r\n -->\r\n <p class=\"dpe-field__hint\">\r\n Cambiare tipo toglie dal documento i campi che il tipo nuovo non governa. È una modifica\r\n come le altre: rientra nell’annullamento.\r\n </p>\r\n </div>\r\n\r\n <!--\r\n Due blocchi indipendenti e non un ramo alternativo: per frontend.md §5-ter, normativa,\r\n **un campo che porta un valore si rende anche fuori dalla propria condizione**. Da quando\r\n cambiare tipo ripulisce, il caso arriva solo da un documento scritto altrove — che e’\r\n precisamente il caso per cui quella regola esiste, ed e’ anche l’unico che nessuna\r\n ripulitura puo’ raggiungere. Vuoto e non applicabile resta nascosto.\r\n -->\r\n @if (shows.droppedFields()) {\r\n <div class=\"dpe-field\">\r\n <label class=\"dpe-field__label\" for=\"tr-dropped\">Colonne da eliminare</label>\r\n <input\r\n id=\"tr-dropped\"\r\n type=\"text\"\r\n [value]=\"droppedText()\"\r\n placeholder=\"separate da virgola\"\r\n (change)=\"onDropped(valueOf($event))\"\r\n />\r\n <p class=\"dpe-field__hint\">In ingresso: {{ columnHint() }}</p>\r\n </div>\r\n }\r\n\r\n @if (shows.expressionFields()) {\r\n <section class=\"dpe-section\">\r\n <h4 class=\"dpe-section__title\">Colonne calcolate</h4>\r\n @if (expressionFields().length === 0) {\r\n <p class=\"dpe-empty\">Nessuna colonna calcolata.</p>\r\n }\r\n @for (field of expressionFields(); track $index) {\r\n <div class=\"dpe-section\">\r\n <div class=\"dpe-row\">\r\n <div class=\"dpe-field\">\r\n <label class=\"dpe-field__label\" [attr.for]=\"'tr-alias-' + $index\">Alias</label>\r\n <input\r\n [id]=\"'tr-alias-' + $index\"\r\n type=\"text\"\r\n [value]=\"field.alias\"\r\n (change)=\"patchExpression($index, { alias: valueOf($event) })\"\r\n />\r\n </div>\r\n <button\r\n type=\"button\"\r\n class=\"dpe-btn dpe-btn--danger\"\r\n [attr.aria-label]=\"'Rimuovi la colonna ' + field.alias\"\r\n (click)=\"removeExpression($index)\"\r\n >\r\n Rimuovi\r\n </button>\r\n </div>\r\n\r\n <!--\r\n Le formule del nodo. Le diagnostiche si passano **per riga**: filtrate per solo\r\n nome di campo, l’errore della prima formula comparirebbe sotto la terza.\r\n -->\r\n <dpe-expression-editor\r\n label=\"Formula\"\r\n [value]=\"field.expression\"\r\n [language]=\"language()\"\r\n [columns]=\"columnNames()\"\r\n [issues]=\"issuesOnItem('expressionFields', $index, 'expression')\"\r\n [outcome]=\"catalogOutcome()\"\r\n [requiredForm]=\"requiredForm()\"\r\n [formSupport]=\"formSupport()\"\r\n [isFormUpdating]=\"isFormUpdating()\"\r\n [isCatalogLoading]=\"isCatalogLoading()\"\r\n [controlId]=\"'tr-expr-' + $index\"\r\n (valueChange)=\"patchExpression($index, { expression: $event })\"\r\n />\r\n\r\n <div class=\"dpe-field\">\r\n <span class=\"dpe-field__label\">Tipo dichiarato</span>\r\n <dpe-column-type-editor\r\n [value]=\"field.declaredType\"\r\n [controlId]=\"'tr-type-' + $index\"\r\n (valueChange)=\"patchExpression($index, { declaredType: $event })\"\r\n />\r\n <p class=\"dpe-field__hint\">\r\n <strong>Dichiarato dall’utente</strong>: lo schema resta una funzione della\r\n definizione, quindi la colonna calcolata ha il tipo scritto qui e non quello\r\n dedotto dal compilatore. Il compilatore lo <strong>verifica</strong> e lo segnala\r\n se non torna.\r\n </p>\r\n </div>\r\n </div>\r\n }\r\n <div>\r\n <button type=\"button\" class=\"dpe-btn\" (click)=\"addExpression()\">Aggiungi colonna</button>\r\n </div>\r\n </section>\r\n }\r\n\r\n @if (shows.partitionBy() || shows.orderBy()) {\r\n <section class=\"dpe-section\">\r\n <h4 class=\"dpe-section__title\">Finestra</h4>\r\n <div class=\"dpe-field\">\r\n <label class=\"dpe-field__label\" for=\"tr-partition\">Partiziona per</label>\r\n <input\r\n id=\"tr-partition\"\r\n type=\"text\"\r\n [value]=\"partitionText()\"\r\n placeholder=\"colonne separate da virgola\"\r\n (change)=\"onPartition(valueOf($event))\"\r\n />\r\n </div>\r\n <div class=\"dpe-field\">\r\n <span class=\"dpe-field__label\">Ordina per</span>\r\n @for (order of orderBy(); track $index) {\r\n <div class=\"dpe-row\">\r\n <input\r\n type=\"text\"\r\n list=\"tr-columns\"\r\n [value]=\"order.columnName\"\r\n [attr.aria-label]=\"'Colonna di ordinamento ' + ($index + 1)\"\r\n (change)=\"patchOrder($index, { columnName: valueOf($event) })\"\r\n />\r\n <select\r\n [dpeValue]=\"order.direction ?? 'Ascending'\"\r\n [attr.aria-label]=\"'Direzione ' + ($index + 1)\"\r\n (change)=\"patchOrder($index, { direction: valueOf($event) === 'Descending' ? 'Descending' : 'Ascending' })\"\r\n >\r\n <option value=\"Ascending\">Ascendente</option>\r\n <option value=\"Descending\">Discendente</option>\r\n </select>\r\n <button\r\n type=\"button\"\r\n class=\"dpe-btn dpe-btn--icon dpe-btn--danger\"\r\n aria-label=\"Rimuovi l’ordinamento\"\r\n (click)=\"removeOrder($index)\"\r\n >\r\n ×\r\n </button>\r\n </div>\r\n }\r\n <div>\r\n <button type=\"button\" class=\"dpe-btn\" (click)=\"addOrder()\">Aggiungi ordinamento</button>\r\n </div>\r\n </div>\r\n </section>\r\n }\r\n\r\n <datalist id=\"tr-columns\">\r\n @for (column of inputColumns(); track $index) {\r\n <option [value]=\"column.name\">{{ column.type.kind }}</option>\r\n }\r\n </datalist>\r\n </div>\r\n `,\r\n})\r\nexport class DpeTransformInspectorComponent extends DpeNodeInspectorBase<TransformNode> {\r\n override readonly collection: DpeNodeCollectionName = 'transforms';\r\n\r\n protected readonly kinds = KINDS;\r\n\r\n private readonly validation = inject(DpeValidationService);\r\n private readonly catalogs = inject(DpeFormulaCatalogStore);\r\n\r\n /**\r\n * Il nome del linguaggio di formule, dall'ambiente (ADR 0031/0036). Non e' il dialetto: il\r\n * dialetto non c'entra piu' con cio' che si scrive in quel campo, e nominarlo li' era gia'\r\n * fuorviante prima che le espressioni smettessero di essere SQL.\r\n */\r\n readonly language = computed(() => this.validation.expressionLanguage());\r\n\r\n /**\r\n * L'esito del catalogo **di questo nodo**: i simboli dipendono dallo schema a monte, e i tre\r\n * esiti del contratto si rendono diversi perche' sono diversi (`contracts.md` §7).\r\n */\r\n readonly catalogOutcome = computed(() => this.catalogs.outcomeOf(this.nodeName()));\r\n\r\n /*\r\n * `requiredForm` sta sulla base: arriva con lo **schema** del nodo, cioe' con ogni validazione, e\r\n * non con il catalogo. Cosi' c'e' anche quando il catalogo non si chiede affatto — l'ospite che\r\n * dichiara di non descrivere il proprio linguaggio — che e' il caso in cui l'incrocio con le\r\n * forme supportate serve di piu' e prima non scattava mai.\r\n */\r\n\r\n /**\r\n * L'incrocio fra la forma richiesta da **questo nodo** e le forme del compilatore **di questo\r\n * ambiente**: due sorgenti diverse, due vite diverse, ed e' l'editor che le mette insieme.\r\n * Quando non si incontrano, il campo formula non si scrive e dice perche'.\r\n */\r\n readonly formSupport = computed(() =>\r\n dpeExpressionFormSupport(this.requiredForm(), this.validation.supportedForms()),\r\n );\r\n\r\n /**\r\n * Una validazione e' in volo, quindi `requiredForm` puo' non descrivere ancora il documento che\r\n * l'utente ha davanti (ADR 0042). E' il caso che questa tendina produce: cambiare `kind` cambia\r\n * la forma richiesta **senza cambiare lo schema in uscita**, cioe' senza nessun altro segnale\r\n * che l'editor possa leggere in locale.\r\n *\r\n * Si passa il segnale nudo del servizio e non un incrocio, perche' non c'e' niente da\r\n * incrociare: e' uno stato **del documento** e **transitorio**, non un fatto su questo nodo.\r\n */\r\n readonly isFormUpdating = computed(() => this.validation.isValidating());\r\n\r\n readonly isCatalogLoading = computed(() => this.catalogs.isLoading(this.nodeName()));\r\n\r\n readonly expressionFields = computed<readonly ExpressionField[]>(\r\n () => this.node().expressionFields ?? [],\r\n );\r\n\r\n readonly orderBy = computed<readonly OrderByField[]>(() => this.node().orderBy ?? []);\r\n\r\n readonly droppedText = computed(() => (this.node().droppedFields ?? []).join(', '));\r\n readonly partitionText = computed(() => (this.node().partitionBy ?? []).join(', '));\r\n\r\n /*\r\n * I quattro campi condizionali della trasformazione, uno per voce della tabella e tutti con la\r\n * stessa forma: **la condizione vale oppure il campo porta un valore** (`frontend.md` §5-ter).\r\n * Ogni controllo sa gia' tornare all'assenza — il testo si svuota, le righe hanno il proprio ×,\r\n * e la collezione vuota vale assente come la stringa vuota.\r\n *\r\n * La finestra resta **un blocco solo** per due campi, quindi nel template la guardia e'\r\n * sull'uno **o** sull'altro: un `orderBy` rimasto su una `Expression` deve comparire anche\r\n * senza un `partitionBy` accanto.\r\n */\r\n readonly shows = dpeFieldVisibility(this.node, DPE_TRANSFORM_CONDITIONS);\r\n\r\n readonly columnNames = computed(() => this.inputColumns().map((column) => column.name));\r\n\r\n constructor() {\r\n super();\r\n /*\r\n * Il catalogo si chiede **una volta per nodo**, quando la finestra di modifica e' aperta su\r\n * questo nodo e il tipo di trasformazione governa davvero delle formule.\r\n *\r\n * Le dipendenze di questo effect sono **tre signal e nessuna altra**: nodo aperto, nome del\r\n * nodo, tipo che governa le formule. In particolare **non** legge la definizione — quella la\r\n * legge `requestFor`, in `untracked`, e leggerla qui significherebbe una richiesta di catalogo\r\n * a ogni battitura. E' il modo naturale di scriverlo e quello che `frontend.md` §5-quater\r\n * vieta: ogni richiesta costa al backend una passata completa di grafo e metadati.\r\n *\r\n * Il gesto di selezionare un nodo sul canvas non basta: l'ispettore esiste anche a finestra\r\n * chiusa (la dialog e' nel guscio e il contenuto resta montato), quindi senza la guardia\r\n * sull'apertura si pagherebbe un catalogo per ogni clic sul canvas.\r\n */\r\n effect(() => {\r\n if (this.store.isNodeEditorOpen() && this.shows.expressionFields()) {\r\n this.catalogs.requestFor(this.nodeName());\r\n }\r\n });\r\n }\r\n\r\n readonly columnHint = computed(() => {\r\n const names = this.columnNames();\r\n return names.length > 10 ? `${names.slice(0, 10).join(', ')}, …` : names.join(', ') || '—';\r\n });\r\n\r\n /**\r\n * Cambiare tipo **ripulisce** i campi che il tipo nuovo non governa. Fuori dalla propria\r\n * condizione il backend risponde `DPE_MODEL_CONDITIONAL_FORBIDDEN`, e lasciare quei valori\r\n * significa consegnare all'autore un rilievo per campo dopo un gesto con cui ha appena\r\n * dichiarato che quei campi non valgono piu'.\r\n *\r\n * Quali siano quei campi **non e' scritto qui**: lo dice `DPE_TRANSFORM_CONDITIONS`, la stessa\r\n * tabella da cui nasce `shows`. Prima erano due elenchi gemelli, e una divergenza fra i due\r\n * avrebbe prodotto un campo ripulito che l'ispettore continua a mostrare — o l'opposto — senza\r\n * che niente lo segnalasse.\r\n *\r\n * La ripulitura resta **piu' stretta di un azzeramento**: `Expression` ↔ `ComputeRelative` non\r\n * tocca `expressionFields`, perche' quella riga della tabella e' vera per entrambi i tipi.\r\n */\r\n protected onKind(value: string): void {\r\n this.patchPruned({ kind: value as TransformKind }, DPE_TRANSFORM_CONDITIONS);\r\n }\r\n\r\n protected addExpression(): void {\r\n this.patch({\r\n expressionFields: [\r\n ...this.expressionFields(),\r\n { alias: '', expression: '', declaredType: { kind: 'String' } },\r\n ],\r\n });\r\n }\r\n\r\n protected removeExpression(index: number): void {\r\n this.patch({\r\n expressionFields: this.expressionFields().filter((_, position) => position !== index),\r\n });\r\n }\r\n\r\n protected patchExpression(index: number, patch: Partial<ExpressionField>): void {\r\n this.patch({\r\n expressionFields: this.expressionFields().map((field, position) =>\r\n position === index ? { ...field, ...patch } : field,\r\n ),\r\n });\r\n }\r\n\r\n protected onDropped(raw: string): void {\r\n this.patch({ droppedFields: this.split(raw) });\r\n }\r\n\r\n protected onPartition(raw: string): void {\r\n this.patch({ partitionBy: this.split(raw) });\r\n }\r\n\r\n protected addOrder(): void {\r\n this.patch({ orderBy: [...this.orderBy(), { columnName: '' }] });\r\n }\r\n\r\n protected removeOrder(index: number): void {\r\n this.patch({ orderBy: this.orderBy().filter((_, position) => position !== index) });\r\n }\r\n\r\n protected patchOrder(index: number, patch: Partial<OrderByField>): void {\r\n this.patch({\r\n orderBy: this.orderBy().map((order, position) =>\r\n position === index ? { ...order, ...patch } : order,\r\n ),\r\n });\r\n }\r\n\r\n private split(raw: string): string[] | undefined {\r\n const values = raw\r\n .split(',')\r\n .map((value) => value.trim())\r\n .filter((value) => value.length > 0);\r\n return values.length > 0 ? values : undefined;\r\n }\r\n}\r\n","/**\r\n * Ispettore della scrittura.\r\n *\r\n * `target` decide **cosa produce la libreria**: un'istruzione SQL per l'host (`Database`) oppure\r\n * un file (`Json`, `Csv`). E' la cosa piu' importante di questo nodo, quindi sta in cima, e i\r\n * campi che seguono cambiano di conseguenza: tabella e operazione per `Database`, opzioni di\r\n * formato per il file corrispondente.\r\n *\r\n * «Di conseguenza» con un'eccezione che vale per **tutti** i campi condizionali di qui — le otto\r\n * voci di `DPE_WRITEBACK_CONDITIONS` — ed e' normativa, frontend.md §5-ter: **un campo che porta\r\n * un valore si rende anche fuori dalla propria condizione**. Vuoto e non applicabile resta\r\n * nascosto.\r\n *\r\n * Le condizioni non sono scritte qui: stanno in `core/dpe-conditional-fields.ts`, **un punto solo**\r\n * letto sia dalla resa sia dalla ripulitura di `onTarget`. Prima erano due elenchi gemelli in\r\n * questo stesso file, e divergevano gia' su `externalIdFieldName`.\r\n *\r\n * Quell'elenco e' completo di proposito, e comprende i due campi su cui il backend **non** ha\r\n * nessuna regola: `tableName` fuori da `Database` e `onlyChangedRows` su un artefatto non sono\r\n * vietati da niente. Far dipendere la resa dalla presenza di una diagnostica accoppierebbe questo\r\n * template all'elenco dei codici, cioe' gli farebbe sapere una regola di dominio; e i due campi\r\n * muti sono il caso peggiore, non quello trascurabile — nessuno li segnala e un cambio di\r\n * destinazione li puo' rendere attivi di colpo.\r\n *\r\n * Due avvisi che non sono decorazione:\r\n *\r\n * - `Insert`/`Update`/`Upsert`/`Delete` **non vengono eseguiti** dalla libreria: determinano la\r\n * forma dell'SQL di lettura consegnato all'host. Un'interfaccia che li presentasse come azioni\r\n * farebbe credere che la pipeline scriva sul database;\r\n * - `onlyChangedRows` e' **dichiarato e non implementato** da noi: il change tracking e' dell'host\r\n * (ADR 0011) e non influenza l'SQL generato.\r\n *\r\n * Della stessa specie sono `processingGroupFieldName` e `failureHandling` (ADR 0040): l'autore li\r\n * scrive, l'host li rispetta, la libreria non li esegue. `failureHandling` si puo' lasciare **non\r\n * dichiarata**, ed e' uno stato vero del documento: chi non la dichiara non deve guadagnarla\r\n * passando di qui (**I7**).\r\n */\r\n\r\nimport { ChangeDetectionStrategy, Component, computed, effect, inject } from '@angular/core';\r\n\r\nimport {\r\n DPE_WRITEBACK_CONDITIONS,\r\n dpeFieldVisibility,\r\n} from '../../core/dpe-conditional-fields';\r\nimport { DpeFormulaCatalogStore } from '../../core/dpe-formula-catalog.store';\r\nimport { dpeExpressionFormSupport } from '../../core/dpe-formula-catalog.view';\r\nimport { DpeValidationService } from '../../core/dpe-validation.service';\r\nimport {\r\n CsvDelimiter,\r\n DpeNodeCollectionName,\r\n WritebackFailureHandling,\r\n WritebackFieldMapping,\r\n WritebackFieldRole,\r\n WritebackNode,\r\n WritebackOperation,\r\n WritebackTargetKind,\r\n} from '../../model/dpe-definition.model';\r\nimport { DpeExpressionEditorComponent } from '../shared/dpe-expression-editor.component';\r\nimport { DpeSelectValueDirective } from '../shared/dpe-select-value.directive';\r\nimport { DpeNodeInspectorBase } from './dpe-node-inspector-base';\r\n\r\nconst TARGETS: readonly WritebackTargetKind[] = ['Database', 'Json', 'Csv'];\r\nconst OPERATIONS: readonly WritebackOperation[] = ['Insert', 'Update', 'Upsert', 'Delete'];\r\nconst ROLES: readonly WritebackFieldRole[] = ['Value', 'PrimaryKey', 'QualifierKey'];\r\nconst FAILURE_HANDLINGS: readonly WritebackFailureHandling[] = ['PerRecord', 'Atomic'];\r\nconst DELIMITERS: readonly CsvDelimiter[] = ['Comma', 'Semicolon', 'Tab', 'Pipe', 'Caret', 'Backquote'];\r\n\r\n@Component({\r\n selector: 'dpe-writeback-inspector',\r\n standalone: true,\r\n imports: [DpeExpressionEditorComponent, DpeSelectValueDirective],\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n template: `\r\n <div class=\"dpe-form\">\r\n <div class=\"dpe-row\">\r\n <div class=\"dpe-field\">\r\n <label class=\"dpe-field__label\" for=\"wb-target\">Destinazione</label>\r\n <select id=\"wb-target\" [dpeValue]=\"node().target\" (change)=\"onTarget(valueOf($event))\">\r\n @for (target of targets; track $index) {\r\n <option [value]=\"target\">{{ target }}</option>\r\n }\r\n </select>\r\n </div>\r\n <div class=\"dpe-field\">\r\n <label class=\"dpe-field__label\" for=\"wb-sequence\">Sequenza</label>\r\n <input\r\n id=\"wb-sequence\"\r\n type=\"number\"\r\n min=\"1\"\r\n [value]=\"node().sequence\"\r\n [attr.aria-invalid]=\"hasIssueOn('sequence')\"\r\n (change)=\"setField('sequence', toNumber(valueOf($event)) ?? 1)\"\r\n />\r\n @for (issue of issuesOn('sequence'); track $index) {\r\n <p class=\"dpe-field__issue\">{{ issue.code }} — {{ issue.message }}</p>\r\n }\r\n </div>\r\n </div>\r\n\r\n <!--\r\n Da qui in giu’ vale la regola di rendering di frontend.md §5-ter, normativa: **un campo che\r\n porta un valore si rende anche fuori dalla propria condizione**; vuoto e non applicabile\r\n resta nascosto, cosi’ il caso normale non guadagna rumore.\r\n\r\n Non e’ una questione di completezza: un valore che nessun controllo mostra non e’\r\n cancellabile dall’editor e per **I7** sopravvive a ogni salvataggio, quindi la definizione\r\n resta invalida per sempre e non riparabile da chi la sta guardando, in silenzio.\r\n\r\n Nessuna decorazione inventata qui: il controllo e’ lo stesso che si vedrebbe se la\r\n condizione valesse, e a dire perche’ non va e’ la diagnostica del backend sotto il campo.\r\n -->\r\n @if (isDatabase()) {\r\n <p class=\"dpe-note\">\r\n La libreria <strong>non scrive</strong> sul database: produce un’istruzione SQL di\r\n lettura e la consegna all’host, che la esegue. L’operazione scelta determina la forma di\r\n quell’istruzione (quali colonne, quale chiave di correlazione).\r\n </p>\r\n }\r\n\r\n @if (shows.tableName() || shows.operation()) {\r\n <div class=\"dpe-row\">\r\n @if (shows.tableName()) {\r\n <div class=\"dpe-field\">\r\n <label class=\"dpe-field__label\" for=\"wb-table\">Tabella di destinazione</label>\r\n <!--\r\n Fuori da Database il backend **non** dice niente su questo campo: nessuna regola\r\n lo vieta, quindi il valore non e’ «invalido», e’ ignorato. La regola di rendering\r\n non guarda le diagnostiche (§5-ter): un nome di tabella su un writeback di file\r\n non lo segnala nessuno, non si vede da nessuna schermata, e un cambio di\r\n destinazione lo rimette in gioco di colpo.\r\n -->\r\n <input\r\n id=\"wb-table\"\r\n type=\"text\"\r\n [value]=\"node().tableName ?? ''\"\r\n [attr.aria-invalid]=\"hasIssueOn('tableName')\"\r\n (change)=\"setField('tableName', valueOf($event))\"\r\n />\r\n @for (issue of issuesOn('tableName'); track $index) {\r\n <p class=\"dpe-field__issue\">{{ issue.code }} — {{ issue.message }}</p>\r\n }\r\n </div>\r\n }\r\n @if (shows.operation()) {\r\n <div class=\"dpe-field\">\r\n <label class=\"dpe-field__label\" for=\"wb-operation\">Operazione</label>\r\n <!--\r\n La voce vuota c’e’ **sempre**, non solo fuori condizione: «operazione assente» e’\r\n uno stato vero del documento — l’unico ammesso su un target di file — e senza di\r\n essa il valore scritto fuori posto non si potrebbe togliere, che e’ il difetto che\r\n questa regola esiste per chiudere. Su Database l’assenza e’ un errore, e il backend\r\n lo dice qui sotto: dirlo e’ suo, non nostro. Toglie anche una bugia vecchia, la\r\n tendina che mostrava «Insert» su un documento che non dichiarava niente.\r\n -->\r\n <select\r\n id=\"wb-operation\"\r\n [dpeValue]=\"node().operation ?? ''\"\r\n [attr.aria-invalid]=\"hasIssueOn('operation')\"\r\n (change)=\"setField('operation', valueOf($event))\"\r\n >\r\n <option value=\"\">Non dichiarata</option>\r\n @for (operation of operations; track $index) {\r\n <option [value]=\"operation\">{{ operation }}</option>\r\n }\r\n </select>\r\n @for (issue of issuesOn('operation'); track $index) {\r\n <p class=\"dpe-field__issue\">{{ issue.code }} — {{ issue.message }}</p>\r\n }\r\n </div>\r\n }\r\n </div>\r\n }\r\n\r\n @if (shows.externalIdFieldName()) {\r\n <div class=\"dpe-field\">\r\n <label class=\"dpe-field__label\" for=\"wb-external-id\">Campo di identificazione esterna</label>\r\n <input\r\n id=\"wb-external-id\"\r\n type=\"text\"\r\n [value]=\"node().externalIdFieldName ?? ''\"\r\n [attr.aria-invalid]=\"hasIssueOn('externalIdFieldName')\"\r\n (change)=\"setField('externalIdFieldName', valueOf($event))\"\r\n />\r\n @for (issue of issuesOn('externalIdFieldName'); track $index) {\r\n <p class=\"dpe-field__issue\">{{ issue.code }} — {{ issue.message }}</p>\r\n }\r\n </div>\r\n }\r\n\r\n @if (shows.onlyChangedRows()) {\r\n <!--\r\n Una casella **sa** dire «assente», a patto che spenta tolga la chiave invece di\r\n scrivere «falso»: senza quello il controllo comparirebbe fuori condizione senza poter\r\n togliere niente. Anche qui il backend tace — non c’e’ nessuna regola su questo campo\r\n fuori da Database — e la regola di rendering non lo chiede.\r\n -->\r\n <label class=\"dpe-check\">\r\n <input\r\n id=\"wb-only-changed\"\r\n type=\"checkbox\"\r\n [checked]=\"node().onlyChangedRows ?? false\"\r\n (change)=\"setFlag('onlyChangedRows', checkedOf($event))\"\r\n />\r\n Solo le righe cambiate\r\n </label>\r\n <p class=\"dpe-field__hint\">\r\n Dichiarazione per l’host: il confronto con lo stato precedente non lo fa la libreria e\r\n non cambia l’SQL generato.\r\n </p>\r\n }\r\n\r\n @if (shows.processingGroupFieldName() || shows.failureHandling()) {\r\n <div class=\"dpe-row\">\r\n @if (shows.processingGroupFieldName()) {\r\n <div class=\"dpe-field\">\r\n <label class=\"dpe-field__label\" for=\"wb-group-field\">Campo di raggruppamento</label>\r\n <!--\r\n Testo con datalist e non una tendina: il valore e' un nome di campo di destinazione\r\n authored, che puo' riferirsi a una mappatura non ancora scritta o appena rinominata.\r\n Una tendina che non trovasse l'opzione mostrerebbe la prima voce al posto del valore\r\n vero, che e' il difetto per cui esiste la direttiva dpeValue — qui si evita invece\r\n di ripararlo.\r\n -->\r\n <input\r\n id=\"wb-group-field\"\r\n type=\"text\"\r\n list=\"wb-target-fields\"\r\n [value]=\"node().processingGroupFieldName ?? ''\"\r\n [attr.aria-invalid]=\"hasIssueOn('processingGroupFieldName')\"\r\n (change)=\"setField('processingGroupFieldName', valueOf($event))\"\r\n />\r\n @for (issue of issuesOn('processingGroupFieldName'); track $index) {\r\n <p class=\"dpe-field__issue\">{{ issue.code }} — {{ issue.message }}</p>\r\n }\r\n </div>\r\n }\r\n @if (shows.failureHandling()) {\r\n <div class=\"dpe-field\">\r\n <label class=\"dpe-field__label\" for=\"wb-failure-handling\">Gestione del fallimento</label>\r\n <!--\r\n L'opzione vuota non e' un segnaposto: «non dichiarata» e' uno stato del documento,\r\n distinto da entrambe le dichiarazioni, e deve restare scegliibile per tornare\r\n indietro. Sceglierla toglie la chiave dal documento invece di scriverci una stringa\r\n vuota, quindi un salva-dopo-carica rispedisce il documento identico (I7). Ed e'\r\n anche il solo modo di togliere il valore quando il campo si rende fuori condizione.\r\n -->\r\n <select\r\n id=\"wb-failure-handling\"\r\n [dpeValue]=\"node().failureHandling ?? ''\"\r\n [attr.aria-invalid]=\"hasIssueOn('failureHandling')\"\r\n (change)=\"setField('failureHandling', valueOf($event))\"\r\n >\r\n <option value=\"\">Non dichiarata</option>\r\n @for (handling of failureHandlings; track $index) {\r\n <option [value]=\"handling\">{{ handling }}</option>\r\n }\r\n </select>\r\n @for (issue of issuesOn('failureHandling'); track $index) {\r\n <p class=\"dpe-field__issue\">{{ issue.code }} — {{ issue.message }}</p>\r\n }\r\n </div>\r\n }\r\n </div>\r\n <p class=\"dpe-field__hint\">\r\n Due dichiarazioni per l’host, senza effetto sull’SQL: il campo di raggruppamento dice con\r\n quale colonna spezzare il lavoro in lotti, <code>Atomic</code> obbliga a scrivere tutto o\r\n niente e <code>PerRecord</code> consente il successo parziale. Lasciata non dichiarata,\r\n la regola effettiva la stabilisce il backend — un writeback dentro un gruppo atomico\r\n resta atomico comunque.\r\n </p>\r\n <!--\r\n Lo Step porta la regola effettiva **solo** dove l’host scrive davvero: su un artefatto il\r\n campo e’ assente di proposito, e su un artefatto questa riga tace da se’ — la guardia e’\r\n sul valore, non sul ramo, quindi regge anche ora che i due campi si rendono fuori dal\r\n ramo Database. Tace anche prima della prima risposta del backend, che e’ meglio di una\r\n regola indovinata in locale.\r\n -->\r\n @if (step()?.failureHandling; as regola) {\r\n <p class=\"dpe-field__hint\">\r\n Regola effettiva secondo il backend: <strong>{{ regola }}</strong\r\n >@if (step()?.atomicGroupName; as gruppo) {\r\n , imposta dal gruppo atomico <code>{{ gruppo }}</code\r\n >}.\r\n </p>\r\n }\r\n <datalist id=\"wb-target-fields\">\r\n @for (field of fields(); track $index) {\r\n <option [value]=\"field.targetFieldName\"></option>\r\n }\r\n </datalist>\r\n }\r\n\r\n @if (shows.csvOptions()) {\r\n <div class=\"dpe-row\">\r\n <div class=\"dpe-field\">\r\n <label class=\"dpe-field__label\" for=\"wb-delimiter\">Separatore</label>\r\n <select\r\n id=\"wb-delimiter\"\r\n [dpeValue]=\"node().csvOptions?.delimiter ?? 'Comma'\"\r\n (change)=\"patchCsv({ delimiter: asDelimiter(valueOf($event)) })\"\r\n >\r\n @for (delimiter of delimiters; track $index) {\r\n <option [value]=\"delimiter\">{{ delimiter }}</option>\r\n }\r\n </select>\r\n </div>\r\n <label class=\"dpe-check\">\r\n <input\r\n type=\"checkbox\"\r\n [checked]=\"node().csvOptions?.includesHeaderRow ?? true\"\r\n (change)=\"patchCsv({ includesHeaderRow: checkedOf($event) })\"\r\n />\r\n Riga di intestazione\r\n </label>\r\n <!--\r\n Il comando c’e’ quando **l’oggetto c’e’**, dentro e fuori dalla propria condizione: le\r\n opzioni sono facoltative anche sul target che le ammette (assenti valgono i default), e\r\n senza questo comando un csvOptions scritto su un writeback di database non sarebbe\r\n togliibile — ne’ la tendina ne’ la casella sanno dire «assente», sanno dire solo un\r\n altro valore. E’ la stessa ragione per cui la tendina del fallimento ha la voce vuota.\r\n -->\r\n @if (node().csvOptions) {\r\n <button type=\"button\" class=\"dpe-btn\" (click)=\"removeCsvOptions()\">\r\n Rimuovi le opzioni CSV\r\n </button>\r\n }\r\n </div>\r\n @for (issue of issuesOn('csvOptions'); track $index) {\r\n <p class=\"dpe-field__issue\">{{ issue.code }} — {{ issue.message }}</p>\r\n }\r\n <p class=\"dpe-field__hint\">\r\n Il quoting non e’ configurabile: si quota quando serve, con le virgolette raddoppiate.\r\n </p>\r\n }\r\n\r\n @if (shows.jsonOptions()) {\r\n <div class=\"dpe-row\">\r\n <label class=\"dpe-check\">\r\n <input\r\n type=\"checkbox\"\r\n [checked]=\"node().jsonOptions?.indented ?? false\"\r\n (change)=\"patchJson(checkedOf($event))\"\r\n />\r\n JSON indentato\r\n </label>\r\n @if (node().jsonOptions) {\r\n <button type=\"button\" class=\"dpe-btn\" (click)=\"removeJsonOptions()\">\r\n Rimuovi le opzioni JSON\r\n </button>\r\n }\r\n </div>\r\n @for (issue of issuesOn('jsonOptions'); track $index) {\r\n <p class=\"dpe-field__issue\">{{ issue.code }} — {{ issue.message }}</p>\r\n }\r\n }\r\n\r\n <!--\r\n Il **terzo** sito di authoring di formule (ADR 0031 §1), quello che si dimentica: non e’ un\r\n valore ma un **predicato**, e passa dal compilatore come le colonne calcolate.\r\n -->\r\n <dpe-expression-editor\r\n label=\"Filtro sul risultato (facoltativo)\"\r\n [value]=\"node().filterCondition\"\r\n [language]=\"language()\"\r\n [columns]=\"columnNames()\"\r\n [issues]=\"issuesOn('filterCondition')\"\r\n [outcome]=\"catalogOutcome()\"\r\n [requiredForm]=\"requiredForm()\"\r\n [formSupport]=\"formSupport()\"\r\n [isFormUpdating]=\"isFormUpdating()\"\r\n [isCatalogLoading]=\"isCatalogLoading()\"\r\n controlId=\"wb-filter\"\r\n (valueChange)=\"setField('filterCondition', $event)\"\r\n />\r\n\r\n <section class=\"dpe-section\">\r\n <h4 class=\"dpe-section__title\">Mappatura dei campi</h4>\r\n @if (fields().length === 0) {\r\n <p class=\"dpe-empty\">Nessun campo mappato.</p>\r\n } @else {\r\n <table class=\"dpe-table\">\r\n <thead>\r\n <tr>\r\n <th>Colonna a monte</th>\r\n <th>Parametro</th>\r\n <th>Campo di destinazione</th>\r\n <th>Ruolo</th>\r\n <th>Auto</th>\r\n <th></th>\r\n </tr>\r\n </thead>\r\n <tbody>\r\n @for (field of fields(); track $index) {\r\n <tr>\r\n <td>\r\n <!-- Colonna e parametro sono esclusivi: scegliere l’uno svuota l’altro. -->\r\n <input\r\n type=\"text\"\r\n list=\"wb-columns\"\r\n [value]=\"field.sourceFieldName ?? ''\"\r\n [disabled]=\"!!field.runtimeParameterName || (field.isAutoGenerated ?? false)\"\r\n [attr.aria-label]=\"'Colonna del campo ' + ($index + 1)\"\r\n (change)=\"patchField($index, { sourceFieldName: valueOf($event) || undefined })\"\r\n />\r\n </td>\r\n <td>\r\n <select\r\n [dpeValue]=\"field.runtimeParameterName ?? ''\"\r\n [attr.aria-label]=\"'Parametro del campo ' + ($index + 1)\"\r\n (change)=\"onRuntimeParameter($index, valueOf($event))\"\r\n >\r\n <option value=\"\">—</option>\r\n @for (parameter of valueParameters(); track $index) {\r\n <option [value]=\"parameter.name\">{{ parameter.name }}</option>\r\n }\r\n </select>\r\n </td>\r\n <td>\r\n <input\r\n type=\"text\"\r\n [value]=\"field.targetFieldName\"\r\n [attr.aria-label]=\"'Campo di destinazione ' + ($index + 1)\"\r\n (change)=\"patchField($index, { targetFieldName: valueOf($event) })\"\r\n />\r\n </td>\r\n <td>\r\n <select\r\n [dpeValue]=\"field.role ?? 'Value'\"\r\n [attr.aria-label]=\"'Ruolo del campo ' + ($index + 1)\"\r\n (change)=\"patchField($index, { role: asRole(valueOf($event)) })\"\r\n >\r\n @for (role of roles; track $index) {\r\n <option [value]=\"role\">{{ role }}</option>\r\n }\r\n </select>\r\n </td>\r\n <td>\r\n <input\r\n type=\"checkbox\"\r\n [checked]=\"field.isAutoGenerated ?? false\"\r\n [attr.aria-label]=\"'Generato dalla destinazione ' + ($index + 1)\"\r\n (change)=\"patchField($index, { isAutoGenerated: checkedOf($event) })\"\r\n />\r\n </td>\r\n <td>\r\n <button\r\n type=\"button\"\r\n class=\"dpe-btn dpe-btn--icon dpe-btn--danger\"\r\n aria-label=\"Rimuovi la mappatura\"\r\n (click)=\"removeField($index)\"\r\n >\r\n ×\r\n </button>\r\n </td>\r\n </tr>\r\n }\r\n </tbody>\r\n </table>\r\n }\r\n <datalist id=\"wb-columns\">\r\n @for (column of inputColumns(); track $index) {\r\n <option [value]=\"column.name\">{{ column.type.kind }}</option>\r\n }\r\n </datalist>\r\n <div>\r\n <button type=\"button\" class=\"dpe-btn\" (click)=\"addField()\">Aggiungi mappatura</button>\r\n </div>\r\n </section>\r\n </div>\r\n `,\r\n})\r\nexport class DpeWritebackInspectorComponent extends DpeNodeInspectorBase<WritebackNode> {\r\n override readonly collection: DpeNodeCollectionName = 'writebacks';\r\n\r\n protected readonly targets = TARGETS;\r\n protected readonly operations = OPERATIONS;\r\n protected readonly roles = ROLES;\r\n protected readonly delimiters = DELIMITERS;\r\n protected readonly failureHandlings = FAILURE_HANDLINGS;\r\n\r\n private readonly validation = inject(DpeValidationService);\r\n private readonly catalogs = inject(DpeFormulaCatalogStore);\r\n\r\n /** Il nome del linguaggio di formule, dall'ambiente (ADR 0031/0036). Non e' il dialetto. */\r\n readonly language = computed(() => this.validation.expressionLanguage());\r\n\r\n /**\r\n * L'esito del catalogo **di questo nodo**: i simboli dipendono dallo schema a monte, e i tre\r\n * esiti del contratto si rendono diversi perche' sono diversi (`contracts.md` §7).\r\n */\r\n readonly catalogOutcome = computed(() => this.catalogs.outcomeOf(this.nodeName()));\r\n\r\n /*\r\n * `requiredForm` sta sulla base: arriva con lo **schema** del nodo, cioe' con ogni validazione, e\r\n * non con il catalogo — che per un writeback puo' benissimo non essere mai chiesto.\r\n */\r\n\r\n /**\r\n * L'incrocio fra la forma richiesta da **questo nodo** — qui e' sempre un predicato — e le forme\r\n * che il compilatore di **questo ambiente** sa produrre. Se il predicato non e' fra quelle, il\r\n * filtro sul risultato non si scrive e il campo dice perche'.\r\n */\r\n readonly formSupport = computed(() =>\r\n dpeExpressionFormSupport(this.requiredForm(), this.validation.supportedForms()),\r\n );\r\n\r\n /**\r\n * Come sulla trasformazione: mentre una validazione e' in volo la pastiglia della forma non si\r\n * afferma (ADR 0042). Qui la forma non cambia mai — un writeback pretende sempre un predicato —\r\n * ma il segnale si passa **lo stesso**: che oggi sia costante e' una proprieta' del backend, non\r\n * di questo componente, e sceglierne l'assenza in base a quella proprieta' significherebbe\r\n * riscrivere qui la regola che decide la forma.\r\n */\r\n readonly isFormUpdating = computed(() => this.validation.isValidating());\r\n\r\n readonly isCatalogLoading = computed(() => this.catalogs.isLoading(this.nodeName()));\r\n\r\n readonly isDatabase = computed(() => this.node().target === 'Database');\r\n\r\n constructor() {\r\n super();\r\n /*\r\n * Una volta per nodo, e solo a finestra aperta: l'ispettore resta montato anche quando la\r\n * dialog e' chiusa, quindi senza la guardia ogni clic di selezione sul canvas costerebbe al\r\n * backend una passata completa di grafo e metadati (`frontend.md` §5-quater).\r\n *\r\n * `requestFor` legge la definizione in `untracked`: leggerla qui renderebbe questo effect\r\n * dipendente dal documento, cioe' una richiesta a ogni battitura.\r\n */\r\n effect(() => {\r\n if (this.store.isNodeEditorOpen()) {\r\n this.catalogs.requestFor(this.nodeName());\r\n }\r\n });\r\n }\r\n\r\n /*\r\n * Gli otto campi condizionali di questo nodo, uno per voce di `DPE_WRITEBACK_CONDITIONS` e tutti\r\n * con la stessa forma: **la condizione vale oppure il campo porta un valore** (frontend.md\r\n * §5-ter).\r\n *\r\n * Stanno qui e non nel template perche' la condizione e' una sola idea detta otto volte: nel\r\n * template sarebbero otto espressioni composte da rileggere una per una, e la nona scritta fra\r\n * un anno si dimenticherebbe la meta' «oppure porta un valore» — che e' esattamente il modo in\r\n * cui questo difetto e' nato.\r\n *\r\n * La condizione **non** e' la regola di dominio: dire che il valore e' sbagliato tocca al\r\n * backend, e lo dice con `DPE_MODEL_CONDITIONAL_FORBIDDEN` sotto il campo. Qui si decide solo\r\n * se un controllo esiste sullo schermo, e per questo la meta' che conta guarda il documento, non\r\n * la validita'.\r\n */\r\n readonly shows = dpeFieldVisibility(this.node, DPE_WRITEBACK_CONDITIONS);\r\n\r\n /**\r\n * Lo `Step` di questo writeback, come il backend lo ha consegnato con la validazione.\r\n *\r\n * Serve a una cosa sola e importante: mostrare la gestione del fallimento **efficace**. Quel\r\n * valore e' il risultato di tre regole — appartenenza a un gruppo atomico, valore dichiarato,\r\n * default — e ricostruirle qui sarebbe reimplementare dominio nell'editor. Finche' la\r\n * validazione non e' tornata non c'e' nessuno `Step` e non si mostra niente, che e' meglio di\r\n * un valore indovinato in locale.\r\n */\r\n readonly step = computed(() =>\r\n this.store.writebackSequence().find((entry) => entry.writebackName === this.nodeName()),\r\n );\r\n\r\n readonly fields = computed<readonly WritebackFieldMapping[]>(() => this.node().fields ?? []);\r\n\r\n readonly columnNames = computed(() => this.inputColumns().map((column) => column.name));\r\n\r\n readonly valueParameters = computed(() =>\r\n this.parameters().filter((parameter) => parameter.role === 'Value'),\r\n );\r\n\r\n protected asRole(value: string): WritebackFieldRole {\r\n return value as WritebackFieldRole;\r\n }\r\n\r\n protected asDelimiter(value: string): CsvDelimiter {\r\n return value as CsvDelimiter;\r\n }\r\n\r\n /**\r\n * Cambiare destinazione **toglie** i campi che la destinazione nuova non governa: `operation` e'\r\n * richiesta per `Database` e deve essere assente per `Json`/`Csv`, quindi lasciarla scritta\r\n * produrrebbe `DPE_MODEL_CONDITIONAL_FORBIDDEN`.\r\n *\r\n * La ragione che questo metodo aveva prima — «su un campo che l'utente non vede piu'» — **non\r\n * vale piu'** da §5-ter: adesso quei valori resterebbero visibili e togliibili a mano. Quella\r\n * che resta, e che l'architetto ha confermato: cambiare destinazione e' un gesto **deliberato**\r\n * con cui l'autore dichiara un'altra forma del nodo, e ripulire esegue quell'intento invece di\r\n * regalargli un rilievo per campo da chiudere uno per uno.\r\n *\r\n * **Quali** campi escano non e' scritto qui: lo dice `DPE_WRITEBACK_CONDITIONS`, la stessa\r\n * tabella da cui nasce `shows`. `onlyChangedRows` e' dentro come gli altri — il backend su quel\r\n * campo non ha nessuna regola e la sua casella restava accesa su un artefatto senza che nessuno\r\n * la segnalasse — e **`externalIdFieldName` segue `operation`, non `target`**: e' la condizione\r\n * del backend (`Operation == Upsert`), ed e' il punto su cui i due elenchi gemelli di prima\r\n * divergevano gia'.\r\n *\r\n * L'unica cosa che resta scritta qui e' il **valore iniziale** dell'operazione, che non e' una\r\n * condizione: scegliere `Database` dichiara che un'operazione ci vuole, e la tendina senza\r\n * default direbbe «Non dichiarata» su un nodo appena portato li'. Si semina solo se manca,\r\n * cosi' un `Upsert` scritto altrove sopravvive al passaggio — e con esso il suo campo di\r\n * identificazione esterna.\r\n *\r\n * **Ripulire e' togliere la chiave**, non scrivere `false`: e' la stessa disciplina di\r\n * `setFlag()`, e senza di essa un `onlyChangedRows: false` resterebbe nel documento a nome di\r\n * nessuno (**I7**).\r\n *\r\n * Cio' che rende la scelta non distruttiva e' che questa ripulitura passa da `patch`, quindi da\r\n * `DpeDocumentStore.updateNode` → `commit`, che mette l'istantanea **precedente** nella\r\n * cronologia: «Annulla» rimette i valori. Verificato a schermo, non dedotto. Se non fosse cosi'\r\n * sarebbe una perdita di dati irreversibile su un gesto di routine, e il metodo non dovrebbe\r\n * esistere.\r\n */\r\n protected onTarget(value: string): void {\r\n const target = value as WritebackTargetKind;\r\n const change: Partial<WritebackNode> =\r\n target === 'Database' ? { target, operation: this.node().operation ?? 'Insert' } : { target };\r\n this.patchPruned(change, DPE_WRITEBACK_CONDITIONS);\r\n }\r\n\r\n // Lo spread non esiste nelle espressioni dei template Angular: la fusione sta qui.\r\n protected patchJson(indented: boolean): void {\r\n this.patch({ jsonOptions: { ...(this.node().jsonOptions ?? {}), indented } });\r\n }\r\n\r\n /*\r\n * Togliere l'oggetto intero, non svuotarne i campi: `{}` e' un `jsonOptions` dichiarato, e per\r\n * il backend dichiararlo fuori dal proprio target e' `DPE_MODEL_CONDITIONAL_FORBIDDEN` qualunque\r\n * cosa contenga.\r\n */\r\n protected removeJsonOptions(): void {\r\n this.patch({ jsonOptions: undefined });\r\n }\r\n\r\n protected removeCsvOptions(): void {\r\n this.patch({ csvOptions: undefined });\r\n }\r\n\r\n protected patchCsv(patch: Partial<{ delimiter: CsvDelimiter; includesHeaderRow: boolean }>): void {\r\n this.patch({ csvOptions: { ...(this.node().csvOptions ?? {}), ...patch } });\r\n }\r\n\r\n protected addField(): void {\r\n this.patch({ fields: [...this.fields(), { targetFieldName: '' }] });\r\n }\r\n\r\n protected removeField(index: number): void {\r\n this.patch({ fields: this.fields().filter((_, position) => position !== index) });\r\n }\r\n\r\n protected patchField(index: number, patch: Partial<WritebackFieldMapping>): void {\r\n this.patch({\r\n fields: this.fields().map((field, position) =>\r\n position === index ? { ...field, ...patch } : field,\r\n ),\r\n });\r\n }\r\n\r\n protected onRuntimeParameter(index: number, name: string): void {\r\n this.patchField(index, {\r\n runtimeParameterName: name === '' ? undefined : name,\r\n sourceFieldName: name === '' ? this.fields()[index]?.sourceFieldName : undefined,\r\n });\r\n }\r\n}\r\n","/**\r\n * **Solo** i campi modificabili di **un** nodo: i campi comuni, poi l'ispettore della categoria.\r\n *\r\n * Cio' che non c'e' piu', e dove e' andato (ADR 0026): la testata di identita' e' della dialog che\r\n * lo contiene, le due sezioni di fatti in sola lettura sono di `dpe-node-facts`, e il caso «piu' di\r\n * un nodo selezionato» e' una striscia contestuale del guscio. Non c'e' **nessun** booleano di\r\n * modalita' al loro posto: si e' spostato contenuto, non aggiunto un interruttore che cambia il\r\n * significato del componente.\r\n *\r\n * La **rinomina** sta qui e non nei singoli ispettori, per due ragioni. E' identica per tutte le\r\n * categorie, e soprattutto e' un refactoring globale (**I5**): passa dall'unico punto che\r\n * riscrive i riferimenti a valle, e prima di applicarla **dice quante occorrenze tocchera'**.\r\n * Rinominare non e' modificare un campo, e non deve sembrarlo. E' anche l'unica eccezione al\r\n * commit dal vivo: ha una bozza locale perche' ha un bottone, non perche' sia in una dialog.\r\n */\r\n\r\nimport {\r\n ChangeDetectionStrategy,\r\n Component,\r\n computed,\r\n effect,\r\n inject,\r\n signal,\r\n} from '@angular/core';\r\n\r\nimport { DpeDocumentStore } from '../../core/dpe-document.store';\r\nimport { dpeFindNode } from '../../core/dpe-graph';\r\nimport {\r\n AggregateNode,\r\n AppendNode,\r\n AtomicWritebackNode,\r\n CustomNode,\r\n DataSourceNode,\r\n FilterNode,\r\n ForecastNode,\r\n HierarchyPathNode,\r\n JoinNode,\r\n TransformNode,\r\n WritebackNode,\r\n} from '../../model/dpe-definition.model';\r\nimport { DpeAggregateInspectorComponent } from './dpe-aggregate-inspector.component';\r\nimport { DpeAppendInspectorComponent } from './dpe-append-inspector.component';\r\nimport { DpeAtomicWritebackInspectorComponent } from './dpe-atomic-writeback-inspector.component';\r\nimport { DpeCustomNodeInspectorComponent } from './dpe-custom-node-inspector.component';\r\nimport { DpeDataSourceInspectorComponent } from './dpe-data-source-inspector.component';\r\nimport { DpeFilterInspectorComponent } from './dpe-filter-inspector.component';\r\nimport { DpeForecastInspectorComponent } from './dpe-forecast-inspector.component';\r\nimport { DpeHierarchyPathInspectorComponent } from './dpe-hierarchy-path-inspector.component';\r\nimport { DpeJoinInspectorComponent } from './dpe-join-inspector.component';\r\nimport { DpeTransformInspectorComponent } from './dpe-transform-inspector.component';\r\nimport { DpeWritebackInspectorComponent } from './dpe-writeback-inspector.component';\r\n\r\n@Component({\r\n selector: 'dpe-node-inspector',\r\n standalone: true,\r\n imports: [\r\n DpeAggregateInspectorComponent,\r\n DpeAppendInspectorComponent,\r\n DpeAtomicWritebackInspectorComponent,\r\n DpeCustomNodeInspectorComponent,\r\n DpeDataSourceInspectorComponent,\r\n DpeFilterInspectorComponent,\r\n DpeForecastInspectorComponent,\r\n DpeHierarchyPathInspectorComponent,\r\n DpeJoinInspectorComponent,\r\n DpeTransformInspectorComponent,\r\n DpeWritebackInspectorComponent,\r\n ],\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n template: `\r\n @if (located(); as entry) {\r\n <div class=\"dpe-form\">\r\n <div class=\"dpe-field\">\r\n <label class=\"dpe-field__label\" for=\"node-name\">Nome tecnico</label>\r\n <div class=\"dpe-row\">\r\n <input\r\n id=\"node-name\"\r\n type=\"text\"\r\n [value]=\"nameDraft()\"\r\n [attr.aria-invalid]=\"nameDraft().trim().length === 0\"\r\n (input)=\"nameDraft.set(valueOf($event))\"\r\n />\r\n <button\r\n type=\"button\"\r\n class=\"dpe-btn\"\r\n [disabled]=\"!canRename()\"\r\n (click)=\"rename(entry.node.name)\"\r\n >\r\n Rinomina\r\n </button>\r\n </div>\r\n @if (canRename()) {\r\n <p class=\"dpe-note dpe-note--warning\">\r\n La rinomina aggiorna <strong>{{ referenceCount() }}</strong> riferimenti a valle.\r\n I nomi si confrontano in modo esatto: un riferimento che sbaglia le maiuscole non\r\n verrebbe risolto dal backend.\r\n </p>\r\n }\r\n </div>\r\n\r\n <div class=\"dpe-field\">\r\n <label class=\"dpe-field__label\" for=\"node-label\">Etichetta</label>\r\n <input\r\n id=\"node-label\"\r\n type=\"text\"\r\n [value]=\"entry.node.label ?? ''\"\r\n (change)=\"setCommon('label', valueOf($event))\"\r\n />\r\n </div>\r\n\r\n <div class=\"dpe-field\">\r\n <label class=\"dpe-field__label\" for=\"node-description\">Descrizione</label>\r\n <input\r\n id=\"node-description\"\r\n type=\"text\"\r\n [value]=\"entry.node.description ?? ''\"\r\n (change)=\"setCommon('description', valueOf($event))\"\r\n />\r\n </div>\r\n </div>\r\n\r\n <section class=\"dpe-section\">\r\n @switch (entry.collection) {\r\n @case ('dataSources') {\r\n <dpe-data-source-inspector [nodeName]=\"entry.node.name\" [node]=\"asDataSource(entry.node)\" />\r\n }\r\n @case ('filters') {\r\n <dpe-filter-inspector [nodeName]=\"entry.node.name\" [node]=\"asFilter(entry.node)\" />\r\n }\r\n @case ('joins') {\r\n <dpe-join-inspector [nodeName]=\"entry.node.name\" [node]=\"asJoin(entry.node)\" />\r\n }\r\n @case ('aggregates') {\r\n <dpe-aggregate-inspector [nodeName]=\"entry.node.name\" [node]=\"asAggregate(entry.node)\" />\r\n }\r\n @case ('transforms') {\r\n <dpe-transform-inspector [nodeName]=\"entry.node.name\" [node]=\"asTransform(entry.node)\" />\r\n }\r\n @case ('appends') {\r\n <dpe-append-inspector [nodeName]=\"entry.node.name\" [node]=\"asAppend(entry.node)\" />\r\n }\r\n @case ('hierarchyPaths') {\r\n <dpe-hierarchy-path-inspector [nodeName]=\"entry.node.name\" [node]=\"asHierarchy(entry.node)\" />\r\n }\r\n @case ('forecasts') {\r\n <dpe-forecast-inspector [nodeName]=\"entry.node.name\" [node]=\"asForecast(entry.node)\" />\r\n }\r\n @case ('customNodes') {\r\n <dpe-custom-node-inspector [nodeName]=\"entry.node.name\" [node]=\"asCustom(entry.node)\" />\r\n }\r\n @case ('writebacks') {\r\n <dpe-writeback-inspector [nodeName]=\"entry.node.name\" [node]=\"asWriteback(entry.node)\" />\r\n }\r\n @case ('atomicWritebacks') {\r\n <dpe-atomic-writeback-inspector [nodeName]=\"entry.node.name\" [node]=\"asAtomic(entry.node)\" />\r\n }\r\n }\r\n </section>\r\n } @else {\r\n <p class=\"dpe-empty\">Nessun nodo selezionato.</p>\r\n }\r\n `,\r\n})\r\nexport class DpeNodeInspectorComponent {\r\n private readonly store = inject(DpeDocumentStore);\r\n\r\n readonly located = computed(() => {\r\n const name = this.store.selectedNodeName();\r\n return name === undefined ? undefined : dpeFindNode(this.store.definition(), name);\r\n });\r\n\r\n readonly nameDraft = signal('');\r\n\r\n readonly referenceCount = computed(() => {\r\n const entry = this.located();\r\n return entry ? this.store.countReferences(entry.node.name) : 0;\r\n });\r\n\r\n readonly canRename = computed(() => {\r\n const entry = this.located();\r\n const draft = this.nameDraft().trim();\r\n return !!entry && draft.length > 0 && draft !== entry.node.name;\r\n });\r\n\r\n constructor() {\r\n // La bozza del nome segue la selezione: senza, cambiando nodo si resterebbe con il nome\r\n // del precedente in una casella che rinomina.\r\n effect(() => {\r\n const entry = this.located();\r\n this.nameDraft.set(entry?.node.name ?? '');\r\n });\r\n }\r\n\r\n protected rename(currentName: string): void {\r\n this.store.renameNode(currentName, this.nameDraft().trim());\r\n }\r\n\r\n protected setCommon(field: 'label' | 'description', value: string): void {\r\n const entry = this.located();\r\n if (!entry) {\r\n return;\r\n }\r\n this.store.updateNode(entry.collection, entry.node.name, {\r\n [field]: value === '' ? undefined : value,\r\n } as never);\r\n }\r\n\r\n protected valueOf(event: Event): string {\r\n return (event.target as HTMLInputElement).value;\r\n }\r\n\r\n // I cast sono confinati qui: `@switch` restringe la collezione ma non il tipo del nodo, che\r\n // nel modello e' un'unione. Un cast per ramo e' il prezzo, e sbagliarne uno e' un errore di\r\n // compilazione nel template dell'ispettore, non un difetto silenzioso.\r\n protected asDataSource(node: unknown): DataSourceNode {\r\n return node as DataSourceNode;\r\n }\r\n\r\n protected asFilter(node: unknown): FilterNode {\r\n return node as FilterNode;\r\n }\r\n\r\n protected asJoin(node: unknown): JoinNode {\r\n return node as JoinNode;\r\n }\r\n\r\n protected asAggregate(node: unknown): AggregateNode {\r\n return node as AggregateNode;\r\n }\r\n\r\n protected asTransform(node: unknown): TransformNode {\r\n return node as TransformNode;\r\n }\r\n\r\n protected asAppend(node: unknown): AppendNode {\r\n return node as AppendNode;\r\n }\r\n\r\n protected asHierarchy(node: unknown): HierarchyPathNode {\r\n return node as HierarchyPathNode;\r\n }\r\n\r\n protected asForecast(node: unknown): ForecastNode {\r\n return node as ForecastNode;\r\n }\r\n\r\n protected asCustom(node: unknown): CustomNode {\r\n return node as CustomNode;\r\n }\r\n\r\n protected asWriteback(node: unknown): WritebackNode {\r\n return node as WritebackNode;\r\n }\r\n\r\n protected asAtomic(node: unknown): AtomicWritebackNode {\r\n return node as AtomicWritebackNode;\r\n }\r\n}\r\n","/**\r\n * La finestra di modifica di un nodo: elemento `<dialog>` **nativo**, aperto con `showModal()`\r\n * (ADR 0026).\r\n *\r\n * Perche' l'elemento nativo e non un overlay scritto a mano: **Esc**, il **focus trap** (con la\r\n * finestra aperta il Tab non esce), il **fondo inerte** per puntatore *e* tastiera, `::backdrop`\r\n * come pseudo-elemento standard e nessun `z-index` — il top layer sta sopra ogni stacking context\r\n * senza partecipare alla gara. Sono tutte cose che un overlay dovrebbe reimplementare, e il focus\r\n * trap in particolare non si scrive in due righe.\r\n *\r\n * **Una sola istanza** nel guscio, fratello di `.dpe-shell__body`. Resta nel sottoalbero DOM del\r\n * componente e non si trapianta in `document.body`: il top layer e' una promozione **visiva**, non\r\n * un trapianto, quindi gli eventi risalgono l'albero e il `Ctrl+S` del guscio continua a\r\n * funzionare.\r\n *\r\n * **Le modifiche si applicano dal vivo**: gli ispettori scrivono nello store a ogni `(change)`,\r\n * qui non esiste nessuna bozza locale e il piede ha «Chiudi», non «OK / Annulla». Una bozza\r\n * produrrebbe due nozioni di annullamento in disaccordo con ↶ e rilievi che descrivono un\r\n * documento diverso da quello mostrato. Conseguenza dichiarata in interfaccia: **chiudere non\r\n * annulla**.\r\n *\r\n * Il nodo **non arriva per input**: si rideriva da `selectedNodeName()`, cosi' se cambia sotto\r\n * (una rinomina) la dialog lo segue invece di guardare un nome che non esiste piu'.\r\n */\r\n\r\nimport {\r\n ChangeDetectionStrategy,\r\n Component,\r\n ElementRef,\r\n computed,\r\n effect,\r\n inject,\r\n viewChild,\r\n} from '@angular/core';\r\n\r\nimport { DpeDocumentStore } from '../../core/dpe-document.store';\r\nimport { dpeFindNode } from '../../core/dpe-graph';\r\nimport { dpeNodeCategory } from '../../core/dpe-node-catalog';\r\nimport { dpeNodeIconFamilyClass } from '../../core/dpe-node-icons';\r\nimport { DpeNodeFactsComponent } from '../inspector/dpe-node-facts.component';\r\nimport { DpeNodeInspectorComponent } from '../inspector/dpe-node-inspector.component';\r\nimport { DpeNodeIconComponent } from '../shared/dpe-node-icon.component';\r\n\r\n/**\r\n * Contatore di istanza per l'id dell'etichetta. Serve perche' `aria-labelledby` punta a un id, e\r\n * due editor nella stessa pagina sono due dialog: con un id fisso il documento avrebbe due\r\n * elementi con lo stesso id e il nome accessibile della seconda finestra sarebbe quello della\r\n * prima. Non e' una preferenza di stile — un id duplicato e' un difetto che solo uno screen\r\n * reader mostra.\r\n */\r\nlet nextLabelId = 0;\r\n\r\n@Component({\r\n selector: 'dpe-node-dialog',\r\n standalone: true,\r\n imports: [DpeNodeFactsComponent, DpeNodeInspectorComponent, DpeNodeIconComponent],\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n template: `\r\n <!--\r\n L’elemento dialog sta **sempre** nel template, e solo il contenuto e’ condizionato: cosi’\r\n viewChild.required non puo’ fallire e l’effect che apre non deve difendersi da un riferimento\r\n assente. Una dialog vuota e chiusa non rende niente — il foglio dell’agente utente le da’\r\n display: none.\r\n -->\r\n <dialog\r\n #dlg\r\n class=\"dpe-dialog\"\r\n [attr.aria-labelledby]=\"labelId\"\r\n (close)=\"onClose()\"\r\n (cancel)=\"onCancel()\"\r\n (click)=\"onDialogClick($event)\"\r\n (pointerdown)=\"onPointerDown($event)\"\r\n >\r\n @if (located(); as entry) {\r\n <header class=\"dpe-dialog__head\">\r\n <!--\r\n Quarto posto in cui compare il tipo di nodo (palette, card del canvas, ispettore, qui):\r\n stessa icona e stessa classe di famiglia, calcolate dalle stesse funzioni. Un tipo che\r\n cambia aspetto da un posto all’altro e’ un tipo che non si riconosce.\r\n -->\r\n <span class=\"dpe-dialog__icon\" [class]=\"'dpe-dialog__icon ' + familyClass()\">\r\n <dpe-node-icon [collection]=\"entry.collection\" />\r\n </span>\r\n <strong class=\"dpe-dialog__label\" [id]=\"labelId\">\r\n {{ entry.node.label || entry.node.name }}\r\n </strong>\r\n <span class=\"dpe-dialog__category\">{{ categoryLabel() }}</span>\r\n <code class=\"dpe-dialog__name\">{{ entry.node.name }}</code>\r\n <button\r\n type=\"button\"\r\n class=\"dpe-dialog__btn dpe-dialog__btn--icon\"\r\n aria-label=\"Chiudi la finestra di modifica\"\r\n title=\"Chiudi (Esc). Le modifiche sono già nel documento.\"\r\n (click)=\"store.closeNodeEditor()\"\r\n >\r\n ✕\r\n </button>\r\n </header>\r\n\r\n <div class=\"dpe-dialog__body\">\r\n <!-- Colonna larga: i campi. E’ la ragione per cui questa finestra esiste. -->\r\n <div class=\"dpe-dialog__main\">\r\n <dpe-node-inspector />\r\n </div>\r\n\r\n <!--\r\n Colonna stretta: i fatti dal backend su **questo** nodo. Non entra cio’ che riguarda il\r\n grafo — diagnostiche di tutta la definizione, schema di tutti i nodi e SQL generato\r\n hanno gia’ le loro linguette nel pannello inferiore.\r\n -->\r\n <aside class=\"dpe-dialog__side\">\r\n <dpe-node-facts [nodeName]=\"entry.node.name\" />\r\n </aside>\r\n </div>\r\n\r\n <footer class=\"dpe-dialog__foot\">\r\n <!--\r\n «Dopo aver chiuso» non e’ una precisazione di cortesia: con showModal() il fondo e’\r\n **inerte**, quindi il ↶ della barra non e’ premibile mentre questa frase si legge. E’\r\n il comportamento nativo per cui la dialog e’ stata scelta (ADR 0026) — fondo inerte,\r\n Esc e focus trap senza scrivere un overlay — quindi a dover dire il vero e’ la frase,\r\n non il comportamento. Instradare l’annullamento globale da qui dentro sarebbe piu’\r\n comodo e aprirebbe una domanda che va decisa prima: cosa fa questa finestra se\r\n l’annullamento fa sparire il nodo che sta mostrando.\r\n -->\r\n <p class=\"dpe-dialog__note\">\r\n Le modifiche sono <strong>già nel documento</strong>: chiudere non le annulla. Una volta\r\n chiusa questa finestra si torna indietro con ↶ nella barra.\r\n </p>\r\n <button\r\n type=\"button\"\r\n class=\"dpe-dialog__btn dpe-dialog__btn--danger\"\r\n title=\"Elimina il nodo: i riferimenti a valle restano e diventano riferimenti rotti. Eliminare chiude questa finestra, e da lì il nodo si rimette con ↶ nella barra.\"\r\n (click)=\"store.removeNode(entry.node.name)\"\r\n >\r\n Elimina il nodo\r\n </button>\r\n <button type=\"button\" class=\"dpe-dialog__btn\" (click)=\"store.closeNodeEditor()\">\r\n Chiudi\r\n </button>\r\n </footer>\r\n }\r\n </dialog>\r\n `,\r\n})\r\nexport class DpeNodeDialogComponent {\r\n protected readonly store = inject(DpeDocumentStore);\r\n\r\n private readonly dialogRef = viewChild.required<ElementRef<HTMLDialogElement>>('dlg');\r\n\r\n protected readonly labelId = `dpe-node-dialog-label-${(nextLabelId += 1)}`;\r\n\r\n /**\r\n * Il nodo corrente, **riderivato** dallo store come fa il dispatcher degli ispettori. Non si\r\n * conserva il nome: se il nodo viene rinominato, `selectedNodeName()` porta gia' il nome nuovo.\r\n */\r\n protected readonly located = computed(() => {\r\n const name = this.store.selectedNodeName();\r\n return name === undefined ? undefined : dpeFindNode(this.store.definition(), name);\r\n });\r\n\r\n protected readonly categoryLabel = computed(() => {\r\n const entry = this.located();\r\n return entry ? dpeNodeCategory(entry.collection).label : '';\r\n });\r\n\r\n protected readonly familyClass = computed(() => {\r\n const entry = this.located();\r\n return entry ? dpeNodeIconFamilyClass(entry.collection) : '';\r\n });\r\n\r\n /**\r\n * Vero quando il fondo ha ricevuto il `pointerdown` di questo clic. Vedi `onDialogClick`: la\r\n * verifica di coppia non e' prudenza, e' la correzione di un difetto reale.\r\n */\r\n private pointerDownWasOnBackdrop = false;\r\n\r\n constructor() {\r\n effect(() => {\r\n const el = this.dialogRef().nativeElement;\r\n const shouldBeOpen = this.store.isNodeEditorOpen() && this.located() !== undefined;\r\n // La guardia su `el.open` **non e' cosmetica**: `showModal()` su una dialog gia' aperta\r\n // lancia `InvalidStateError`, e questo effect riesegue a ogni mutazione del documento\r\n // (legge `located()`). Senza, aprire e modificare un campo basterebbe a far comparire\r\n // l'eccezione in console.\r\n if (shouldBeOpen && !el.open) {\r\n el.showModal();\r\n } else if (!shouldBeOpen && el.open) {\r\n el.close();\r\n }\r\n });\r\n }\r\n\r\n /**\r\n * **Esc.** Il browser chiede di chiudere e la finestra e' ancora aperta: la richiesta riguarda\r\n * per definizione la sessione corrente, quindi si chiude e non serve nessuna verifica.\r\n *\r\n * Perche' `cancel` e' legato, contro cio' che sembra sufficiente: in Chrome 151 l'evento `close`\r\n * **arriva molto in ritardo** (vedi `onClose`), quindi con il solo `close` lo store restava\r\n * «aperta» dopo Esc — e il sintomo era che **riaprire lo stesso nodo non faceva niente**: il\r\n * signal non cambiava, l'effect non rieseguiva, `showModal()` non veniva mai chiamato. Sul\r\n * bottone del piede non si vedeva, perche' quella via passa dallo store.\r\n *\r\n * `cancel` sarebbe annullabile: non si annulla. La chiusura dell'elemento arriva comunque dal\r\n * browser, e la guardia su `el.open` nell'effect regge in entrambi gli ordini.\r\n */\r\n protected onCancel(): void {\r\n this.store.closeNodeEditor();\r\n }\r\n\r\n /**\r\n * **L'elemento si e' chiuso.** La guardia non e' prudenza: Chrome accoda `close` come task\r\n * dell'elemento e lo consegna anche **dopo** una riapertura — misurato nella demo, la traccia\r\n * degli eventi mostra `close` con `open: true` subito dopo un `showModal()`. Senza la guardia\r\n * quel `close` in ritardo chiudeva la finestra appena aperta, e il sintomo era una dialog che\r\n * «non si apre a volte»: sparisce da sola dopo essere comparsa, e con la finestra aperta si\r\n * riapriva solo alla prima modifica del documento (l'unica cosa che faceva rieseguire l'effect).\r\n *\r\n * Se l'elemento risulta **aperto**, questo `close` riguarda una sessione precedente e si ignora.\r\n * Se risulta chiuso, la notifica e' di questa sessione — e allineare qui lo store e' anche cio'\r\n * che ripara una divergenza, se il browser chiudesse la finestra per vie sue.\r\n */\r\n protected onClose(): void {\r\n if (this.dialogRef().nativeElement.open) {\r\n return;\r\n }\r\n this.store.closeNodeEditor();\r\n }\r\n\r\n protected onPointerDown(event: Event): void {\r\n this.pointerDownWasOnBackdrop = event.target === this.dialogRef().nativeElement;\r\n }\r\n\r\n /**\r\n * Chiusura al clic sul fondo, con la **verifica di coppia** e non con il solo controllo sul\r\n * click. I clic sul velo sono recapitati all'elemento `<dialog>` stesso — ma anche un\r\n * trascinamento di selezione del testo che **inizia** dentro una casella e **finisce** fuori\r\n * produce un `click` con `target` uguale alla dialog. Con la sola verifica sul click, selezionare\r\n * del testo chiuderebbe la finestra.\r\n */\r\n protected onDialogClick(event: Event): void {\r\n if (this.pointerDownWasOnBackdrop && event.target === this.dialogRef().nativeElement) {\r\n this.store.closeNodeEditor();\r\n }\r\n }\r\n}\r\n","/**\r\n * La palette dei nodi.\r\n *\r\n * Il trascinamento verso il canvas usa `fExternalItem` di @foblex/flow: il rilascio emette\r\n * `fCreateNode` con il `fData` e la posizione **gia' nel sistema di coordinate del canvas**.\r\n * Convertirla a mano — che e' cio' che servirebbe con un drag generico — vorrebbe dire rifare la\r\n * trasformazione di zoom e panoramica della libreria, cioe' riscrivere la parte piu' facile da\r\n * sbagliare senza guadagnarci niente. Il **clic** resta la via da tastiera: la palette e'\r\n * navigabile con Tab e Invio, e chi non usa il mouse crea comunque un nodo.\r\n *\r\n * Il catalogo e' **descrittivo**: etichette e numero di sorgenti servono a rendere, non a\r\n * validare. Che un'unione con una sola sorgente sia sbagliata lo dice il backend.\r\n *\r\n * La voce sta su **una riga** — pastiglia con l'icona, etichetta — e la descrizione resta nel\r\n * `title`. La versione precedente ne faceva un paragrafo di tre righe: undici categorie non\r\n * entravano nel riquadro e sette si vedevano senza scorrere, cioe' l'elenco non era un elenco.\r\n */\r\n\r\nimport { ChangeDetectionStrategy, Component, computed, output, signal } from '@angular/core';\r\nimport { FFlowModule } from '@foblex/flow';\r\n\r\nimport { DPE_NODE_CATEGORIES, DpeNodeCategory } from '../../core/dpe-node-catalog';\r\nimport { dpeNodeIconFamilyClass } from '../../core/dpe-node-icons';\r\nimport { DpeNodeCollectionName } from '../../model/dpe-definition.model';\r\nimport { DpeNodeIconComponent } from '../shared/dpe-node-icon.component';\r\n\r\n/** Un gruppo dell'elenco: intestazione e voci. Presentazione, non semantica. */\r\nexport interface DpeNodePaletteGroup {\r\n key: 'sources' | 'transforms' | 'sinks';\r\n label: string;\r\n categories: readonly DpeNodeCategory[];\r\n}\r\n\r\n@Component({\r\n selector: 'dpe-node-palette',\r\n standalone: true,\r\n imports: [FFlowModule, DpeNodeIconComponent],\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n template: `\r\n <h3>Nodi</h3>\r\n <input\r\n type=\"search\"\r\n placeholder=\"Cerca un nodo\"\r\n aria-label=\"Cerca un nodo\"\r\n (input)=\"filter.set(text($event))\"\r\n />\r\n\r\n @for (group of groups(); track group.key) {\r\n <p class=\"dpe-palette__group\">{{ group.label }}</p>\r\n @for (category of group.categories; track category.collection) {\r\n <!--\r\n fExternalItem rende la voce trascinabile sul canvas; fData porta il nome della\r\n collezione, che e’ l’unica cosa che serve per creare il nodo.\r\n -->\r\n <button\r\n type=\"button\"\r\n class=\"dpe-palette__item\"\r\n fExternalItem\r\n [fExternalItemId]=\"category.collection\"\r\n [fData]=\"category.collection\"\r\n [title]=\"titleOf(category)\"\r\n (click)=\"nodeRequested.emit(category.collection)\"\r\n >\r\n <!--\r\n Un solo figlio dentro l’elemento che la libreria tocca: e’ qui che sta il layout della\r\n riga, e non dipende dal display che @foblex/flow riscrive dopo un trascinamento.\r\n -->\r\n <span class=\"dpe-palette__body\">\r\n <span class=\"dpe-palette__icon\" [class]=\"'dpe-palette__icon ' + familyClass(category)\">\r\n <dpe-node-icon [collection]=\"category.collection\" />\r\n </span>\r\n <span class=\"dpe-palette__label\">{{ category.label }}</span>\r\n @if (category.isNotCompilableInV1) {\r\n <span class=\"dpe-palette__flag\">v1</span>\r\n }\r\n </span>\r\n </button>\r\n }\r\n }\r\n\r\n @if (visible().length === 0) {\r\n <p class=\"dpe-palette__empty\">Nessun nodo corrisponde alla ricerca.</p>\r\n }\r\n `,\r\n})\r\nexport class DpeNodePaletteComponent {\r\n /** Clic sulla voce: il nodo si crea al centro dell'area, non dove stava il puntatore. */\r\n readonly nodeRequested = output<DpeNodeCollectionName>();\r\n\r\n protected readonly filter = signal('');\r\n\r\n readonly visible = computed<readonly DpeNodeCategory[]>(() => {\r\n const needle = this.filter().trim().toLowerCase();\r\n if (needle.length === 0) {\r\n return DPE_NODE_CATEGORIES;\r\n }\r\n return DPE_NODE_CATEGORIES.filter(\r\n (category) =>\r\n category.label.toLowerCase().includes(needle) ||\r\n category.description.toLowerCase().includes(needle),\r\n );\r\n });\r\n\r\n /**\r\n * Tre gruppi, nell'ordine in cui si costruisce una pipeline: da dove entrano i dati, cosa ci\r\n * si fa, dove finiscono. I gruppi vuoti non compaiono, altrimenti una ricerca lascerebbe sullo\r\n * schermo intestazioni senza voci.\r\n */\r\n readonly groups = computed<readonly DpeNodePaletteGroup[]>(() => {\r\n const visible = this.visible();\r\n const groups: DpeNodePaletteGroup[] = [\r\n {\r\n key: 'sources',\r\n label: 'Sorgenti',\r\n categories: visible.filter((category) => category.isSource),\r\n },\r\n {\r\n key: 'transforms',\r\n label: 'Trasformazioni',\r\n categories: visible.filter((category) => !category.isSource && !category.isSink),\r\n },\r\n {\r\n key: 'sinks',\r\n label: 'Scritture',\r\n categories: visible.filter((category) => category.isSink),\r\n },\r\n ];\r\n return groups.filter((group) => group.categories.length > 0);\r\n });\r\n\r\n protected familyClass(category: DpeNodeCategory): string {\r\n return dpeNodeIconFamilyClass(category.collection);\r\n }\r\n\r\n /** Il testo che la descrizione non mostra piu' nel corpo della voce. */\r\n protected titleOf(category: DpeNodeCategory): string {\r\n return category.isNotCompilableInV1\r\n ? `${category.description}\\nModellato ma non compilabile nella versione 1.`\r\n : category.description;\r\n }\r\n\r\n protected text(event: Event): string {\r\n return (event.target as HTMLInputElement).value;\r\n }\r\n}\r\n","/**\r\n * Anteprima dell'SQL generato, in **sola lettura**.\r\n *\r\n * E' lo strumento diagnostico principale per chi costruisce una pipeline: e' l'unico posto in\r\n * cui si vede cosa diventera' il grafo. Tre cose che l'interfaccia dice esplicitamente, perche'\r\n * nessuna e' ovvia guardando il testo:\r\n *\r\n * - i writeback `Database` producono un'istruzione **per l'host**, che la libreria non esegue;\r\n * - i writeback `Json`/`Csv` portano l'SQL di **lettura** con cui l'artefatto viene materializzato;\r\n * - i parametri restano **parametri**: il testo mostra i marcatori del dialetto e i valori stanno\r\n * accanto. Non si interpolano nel testo nemmeno per mostrarlo — un'anteprima che mostra SQL\r\n * concatenato insegna a scriverlo.\r\n *\r\n * Se l'host non implementa `compilePreview` il comando e' disabilitato e questo pannello lo dice:\r\n * degrada l'anteprima, non l'editor.\r\n */\r\n\r\nimport { ChangeDetectionStrategy, Component, computed, inject } from '@angular/core';\r\n\r\nimport { DpeDocumentStore } from '../../core/dpe-document.store';\r\nimport { DpeValidationService } from '../../core/dpe-validation.service';\r\nimport { DpeArtifactPlan, DpeCompiledWriteback } from '../../model/dpe-api.model';\r\n\r\n/**\r\n * I tre soli fatti che un writeback compilato e un **piano di artefatto** (ADR 0029) hanno in\r\n * comune, e gli unici che questo pannello mostra. Tipizzare la sezione sul minimo condiviso\r\n * evita un'unione che il template dovrebbe poi discriminare per rendere le stesse tre cose: qui\r\n * cambia il tipo, non il comportamento.\r\n */\r\ntype DpeSqlPreviewItem = Pick<\r\n DpeCompiledWriteback & DpeArtifactPlan,\r\n 'writebackName' | 'sequence' | 'statement'\r\n>;\r\n\r\ninterface DpeSqlSection {\r\n title: string;\r\n hint: string;\r\n writebacks: readonly DpeSqlPreviewItem[];\r\n}\r\n\r\n@Component({\r\n selector: 'dpe-sql-preview',\r\n standalone: true,\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n template: `\r\n <div class=\"dpe-sql__bar\">\r\n <button\r\n type=\"button\"\r\n class=\"dpe-sql__btn\"\r\n [disabled]=\"!validation.isPreviewAvailable() || validation.isCompiling()\"\r\n (click)=\"validation.requestPreview()\"\r\n >\r\n @if (validation.isCompiling()) {\r\n Compilazione in corso…\r\n } @else {\r\n Genera anteprima\r\n }\r\n </button>\r\n @if (!validation.isPreviewAvailable()) {\r\n <span class=\"dpe-sql__note\">\r\n Anteprima non disponibile su questo ambiente: l’host non espone la compilazione.\r\n </span>\r\n } @else {\r\n <span class=\"dpe-sql__note\">Sola lettura: la libreria non esegue nulla.</span>\r\n }\r\n </div>\r\n\r\n @if (result(); as compiled) {\r\n @if (compiled.hasErrors) {\r\n <p class=\"dpe-sql__empty\">\r\n La compilazione si e’ fermata: ci sono errori di validazione, e nessun artefatto parziale\r\n viene prodotto. Guarda il pannello delle diagnostiche.\r\n </p>\r\n }\r\n\r\n @for (section of sections(); track $index) {\r\n @if (section.writebacks.length > 0) {\r\n <div class=\"dpe-sql__section\">\r\n <h4 class=\"dpe-sql__title\">{{ section.title }}</h4>\r\n <p class=\"dpe-sql__note\">{{ section.hint }}</p>\r\n @for (writeback of section.writebacks; track $index) {\r\n <div>\r\n <p class=\"dpe-sql__note\">\r\n <strong>{{ writeback.writebackName }}</strong> · sequenza {{ writeback.sequence }}\r\n </p>\r\n <pre>{{ writeback.statement.text }}</pre>\r\n @if (writeback.statement.parameters?.length) {\r\n <table>\r\n <thead>\r\n <tr>\r\n <th>Parametro</th>\r\n <th>Tipo</th>\r\n <th>Valore</th>\r\n </tr>\r\n </thead>\r\n <tbody>\r\n @for (parameter of writeback.statement.parameters ?? []; track $index) {\r\n <tr>\r\n <td>{{ parameter.name }}</td>\r\n <td>{{ parameter.type }}</td>\r\n <td>{{ display(parameter.value) }}</td>\r\n </tr>\r\n }\r\n </tbody>\r\n </table>\r\n }\r\n </div>\r\n }\r\n </div>\r\n }\r\n }\r\n\r\n @if (isEmpty()) {\r\n <p class=\"dpe-sql__empty\">Nessuna istruzione prodotta.</p>\r\n }\r\n } @else {\r\n <p class=\"dpe-sql__empty\">Nessuna anteprima richiesta.</p>\r\n }\r\n `,\r\n})\r\nexport class DpeSqlPreviewComponent {\r\n private readonly store = inject(DpeDocumentStore);\r\n protected readonly validation = inject(DpeValidationService);\r\n\r\n readonly result = computed(() => this.store.compileResult());\r\n\r\n readonly sections = computed<DpeSqlSection[]>(() => {\r\n const compiled = this.result();\r\n return [\r\n {\r\n title: 'Scritture su database',\r\n hint: 'Istruzione consegnata all’host, che la esegue: la libreria non scrive.',\r\n writebacks: compiled?.databaseWritebacks ?? [],\r\n },\r\n {\r\n title: 'Artefatti (JSON / CSV)',\r\n hint: 'SQL di lettura con cui il file viene materializzato, non ancora eseguito.',\r\n writebacks: compiled?.fileWritebacks ?? [],\r\n },\r\n ];\r\n });\r\n\r\n readonly isEmpty = computed(() =>\r\n this.sections().every((section) => section.writebacks.length === 0),\r\n );\r\n\r\n /** Il valore di un parametro e' opaco: si mostra, non si converte. */\r\n protected display(value: unknown): string {\r\n if (value === null || value === undefined) {\r\n return 'NULL';\r\n }\r\n return typeof value === 'object' ? JSON.stringify(value) : String(value);\r\n }\r\n}\r\n","/**\r\n * La vista dello schema: colonne in ingresso e in uscita, nodo per nodo.\r\n *\r\n * Tutto quello che si vede qui arriva da `Schemas` di `validate` (**I4**: propagazione pura e\r\n * deterministica, del backend). Il frontend non deduce niente — se il backend non ha ancora\r\n * risposto, la vista e' vuota e lo dice.\r\n *\r\n * Le colonne **in ingresso** sono lo schema dei nodi a monte, e servono accanto a quelle in\r\n * uscita per una cosa sola: vedere dove un **alias** cambia il nome di una colonna (**I5**) e\r\n * cosa arriva a valle con quel nome nuovo. Le colonne rinominate sono marcate: e' il punto in cui\r\n * un riferimento a valle sbaglia nome piu' spesso.\r\n */\r\n\r\nimport { ChangeDetectionStrategy, Component, computed, inject } from '@angular/core';\r\n\r\nimport { DpeDocumentStore } from '../../core/dpe-document.store';\r\nimport { dpeAllNodes, dpeNodeSourceReferences } from '../../core/dpe-graph';\r\nimport { dpeNodeCategory } from '../../core/dpe-node-catalog';\r\nimport { DpeColumnInfo } from '../../model/dpe-api.model';\r\n\r\ninterface DpeSchemaRow {\r\n nodeName: string;\r\n categoryLabel: string;\r\n isSelected: boolean;\r\n isCompilable: boolean;\r\n hasSchema: boolean;\r\n inputColumns: readonly DpeColumnInfo[];\r\n outputColumns: readonly DpeColumnInfo[];\r\n /** Colonne in uscita che non esistono in ingresso: alias nuovi o colonne prodotte dal nodo. */\r\n introduced: ReadonlySet<string>;\r\n /** Colonne in ingresso che non arrivano in uscita: eliminate o rinominate. */\r\n dropped: readonly string[];\r\n}\r\n\r\n@Component({\r\n selector: 'dpe-schema-panel',\r\n standalone: true,\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n template: `\r\n @if (!hasResult()) {\r\n <p class=\"dpe-schema__empty\">In attesa della propagazione dello schema dal backend…</p>\r\n }\r\n\r\n @for (row of rows(); track $index) {\r\n <section class=\"dpe-schema__node\" [class.dpe-schema__node--selected]=\"row.isSelected\">\r\n <div class=\"dpe-schema__head\">\r\n <span class=\"dpe-schema__name\">{{ row.nodeName }}</span>\r\n <span class=\"dpe-schema__category\">{{ row.categoryLabel }}</span>\r\n @if (!row.isCompilable) {\r\n <span class=\"dpe-schema__tag\" title=\"Valido, ma non traducibile in SQL\">non compilabile</span>\r\n }\r\n </div>\r\n\r\n @if (row.inputColumns.length > 0) {\r\n <div class=\"dpe-schema__line\">\r\n <span class=\"dpe-schema__label\">In ingresso</span>\r\n @for (column of row.inputColumns; track $index) {\r\n <span\r\n class=\"dpe-col\"\r\n [class.dpe-col--nullable]=\"column.isNullable\"\r\n [class.dpe-col--dropped]=\"row.dropped.includes(column.name)\"\r\n [title]=\"column.type.kind\"\r\n >\r\n {{ column.name }}\r\n </span>\r\n }\r\n </div>\r\n }\r\n\r\n <div class=\"dpe-schema__line\">\r\n <span class=\"dpe-schema__label\">In uscita</span>\r\n @if (!row.hasSchema) {\r\n <!--\r\n «Ignoto», non «nessuna»: il backend non ha derivato lo schema di questo nodo, e\r\n l’elenco vuoto qui accanto non afferma zero colonne (ADR 0041).\r\n -->\r\n <span\r\n class=\"dpe-schema__empty\"\r\n title=\"Il backend non ha potuto derivare lo schema di questo nodo: l’elenco vuoto non afferma zero colonne\"\r\n >schema ignoto</span\r\n >\r\n }\r\n @for (column of row.outputColumns; track $index) {\r\n <span\r\n class=\"dpe-col\"\r\n [class.dpe-col--nullable]=\"column.isNullable\"\r\n [class.dpe-col--new]=\"row.introduced.has(column.name.toLowerCase())\"\r\n [title]=\"column.type.kind + (column.isNullable ? ' · nullabile' : ' · non nulla')\"\r\n >\r\n {{ column.name }}\r\n </span>\r\n }\r\n </div>\r\n </section>\r\n }\r\n\r\n @if (rows().length > 0) {\r\n <p class=\"dpe-schema__empty\">\r\n In evidenza le colonne che il nodo <strong>introduce</strong> (alias nuovi e colonne\r\n calcolate) e barrate quelle che non arrivano a valle. Tratteggiate le nullabili.\r\n </p>\r\n }\r\n `,\r\n})\r\nexport class DpeSchemaPanelComponent {\r\n private readonly store = inject(DpeDocumentStore);\r\n\r\n readonly hasResult = computed(() => this.store.validateResult() !== undefined);\r\n\r\n readonly rows = computed<DpeSchemaRow[]>(() => {\r\n const definition = this.store.definition();\r\n const schemas = this.store.schemaByNode();\r\n const selected = this.store.selectedNodeName();\r\n const executionOrder = this.store.executionOrder();\r\n\r\n const located = dpeAllNodes(definition);\r\n // L'ordine e' quello **topologico** del backend quando c'e': leggere lo schema nell'ordine\r\n // in cui i dati scorrono e' l'unico modo di seguire un alias fino al sink.\r\n const ordered =\r\n executionOrder.length > 0\r\n ? [...located].sort(\r\n (left, right) =>\r\n indexOrLast(executionOrder, left.node.name) -\r\n indexOrLast(executionOrder, right.node.name),\r\n )\r\n : located;\r\n\r\n return ordered.map((entry) => {\r\n const schema = schemas.get(entry.node.name);\r\n const outputColumns = schema?.columns ?? [];\r\n const inputColumns: DpeColumnInfo[] = [];\r\n const seen = new Set<string>();\r\n for (const reference of dpeNodeSourceReferences(entry)) {\r\n if (!reference.isEdge || !reference.sourceName) {\r\n continue;\r\n }\r\n for (const column of schemas.get(reference.sourceName)?.columns ?? []) {\r\n const key = column.name.toLowerCase();\r\n if (!seen.has(key)) {\r\n seen.add(key);\r\n inputColumns.push(column);\r\n }\r\n }\r\n }\r\n const outputNames = new Set(outputColumns.map((column) => column.name.toLowerCase()));\r\n return {\r\n nodeName: entry.node.name,\r\n categoryLabel: dpeNodeCategory(entry.collection).label,\r\n isSelected: entry.node.name === selected,\r\n isCompilable: schema?.isCompilable !== false,\r\n // Si **legge** dal campo, e non si deduce dalla presenza della voce: da ADR 0041 una voce\r\n // c'e' per ogni nodo della definizione, quindi `schema !== undefined` era vero anche a\r\n // schema ignoto e la riga taceva proprio dove doveva parlare. `=== true` perche' assente\r\n // significa `false`: l'ospite che omette i falsi non manda affatto la proprieta'.\r\n hasSchema: schema?.hasSchema === true,\r\n inputColumns,\r\n outputColumns,\r\n introduced: new Set(\r\n outputColumns\r\n .map((column) => column.name.toLowerCase())\r\n .filter((name) => !seen.has(name)),\r\n ),\r\n dropped: inputColumns\r\n .filter((column) => !outputNames.has(column.name.toLowerCase()))\r\n .map((column) => column.name),\r\n };\r\n });\r\n });\r\n}\r\n\r\n/** I nodi fuori dall'ordine di esecuzione (irraggiungibili) finiscono in coda, non in testa. */\r\nfunction indexOrLast(order: readonly string[], name: string): number {\r\n const index = order.indexOf(name);\r\n return index < 0 ? Number.MAX_SAFE_INTEGER : index;\r\n}\r\n","/**\r\n * Il componente radice dell'editor: `<dpe-builder>`.\r\n *\r\n * Tiene i provider — store del documento, layout, validazione, metadati — **a livello di\r\n * componente** e non della root dell'applicazione: due editor sulla stessa pagina sono due\r\n * documenti indipendenti, con la loro cronologia e la loro selezione.\r\n *\r\n * Non conosce HTTP: tutto il traffico passa da `DpeBuilderApi`, che l'ospite fornisce.\r\n */\r\n\r\nimport {\r\n ChangeDetectionStrategy,\r\n Component,\r\n Signal,\r\n computed,\r\n effect,\r\n inject,\r\n input,\r\n output,\r\n signal,\r\n untracked,\r\n viewChild,\r\n} from '@angular/core';\r\n\r\n\r\nimport { DpeDocumentStore } from '../core/dpe-document.store';\r\nimport { DpeFormulaCatalogStore } from '../core/dpe-formula-catalog.store';\r\nimport { DpeLayoutService } from '../core/dpe-layout.service';\r\nimport { DpeMetadataStore } from '../core/dpe-metadata.store';\r\nimport { DpeValidationService } from '../core/dpe-validation.service';\r\nimport { dpeHasCompleteLayout } from '../core/dpe-node-layout';\r\nimport { dpeAllNodes } from '../core/dpe-graph';\r\nimport { DPE_DIALECT_LABELS } from '../core/dpe-node-catalog';\r\nimport { DpeDefinition, DpeNodeCollectionName, SqlDialectKind } from '../model/dpe-definition.model';\r\nimport { DpeCanvasComponent } from './canvas/dpe-canvas.component';\r\nimport { DpeDiagnosticsPanelComponent } from './diagnostics/dpe-diagnostics-panel.component';\r\nimport { DpeNodeDialogComponent } from './dialog/dpe-node-dialog.component';\r\nimport { DpeNodePaletteComponent } from './palette/dpe-node-palette.component';\r\nimport { DpeSqlPreviewComponent } from './preview/dpe-sql-preview.component';\r\nimport { DpeSchemaPanelComponent } from './schema/dpe-schema-panel.component';\r\n\r\n/** Le tre viste in basso: una alla volta, perche' guardano la stessa cosa da tre angoli. */\r\ntype DpeBottomTab = 'diagnostics' | 'schema' | 'sql';\r\n\r\n@Component({\r\n selector: 'dpe-builder',\r\n standalone: true,\r\n imports: [\r\n DpeCanvasComponent,\r\n DpeDiagnosticsPanelComponent,\r\n DpeNodeDialogComponent,\r\n DpeNodePaletteComponent,\r\n DpeSchemaPanelComponent,\r\n DpeSqlPreviewComponent,\r\n ],\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n /*\r\n * Provider **del componente**, mai della root: due editor sulla stessa pagina sono due documenti\r\n * indipendenti, con la loro cronologia, la loro selezione e — da LF6 — la loro cache di\r\n * cataloghi di formule, che e' per nodo e quindi per documento.\r\n */\r\n providers: [\r\n DpeDocumentStore,\r\n DpeLayoutService,\r\n DpeValidationService,\r\n DpeMetadataStore,\r\n DpeFormulaCatalogStore,\r\n ],\r\n /**\r\n * `Ctrl+S` sul **componente**, non su `document`: due editor nella stessa pagina non devono\r\n * salvarsi a vicenda, e un ascoltatore globale installato dalla libreria intercetterebbe la\r\n * scorciatoia anche quando l'utente sta lavorando altrove nell'applicazione ospite.\r\n */\r\n host: { '(keydown)': 'onKeyDown($event)' },\r\n templateUrl: './dpe-builder.component.html',\r\n})\r\nexport class DpeBuilderComponent {\r\n protected readonly store = inject(DpeDocumentStore);\r\n protected readonly validation = inject(DpeValidationService);\r\n private readonly layout = inject(DpeLayoutService);\r\n\r\n /** La definizione da editare. Cambiarne l'identita' ricarica il documento. */\r\n readonly definition = input.required<DpeDefinition>();\r\n\r\n readonly isEditable = input(true);\r\n\r\n /**\r\n * Emessa a ogni mutazione. L'ospite **non deve** rimandarla dentro `definition`: sarebbe un\r\n * ciclo, e ricaricare azzera la cronologia. Serve per salvare, non per pilotare l'editor.\r\n */\r\n readonly definitionChange = output<DpeDefinition>();\r\n\r\n protected readonly dialectLabels = DPE_DIALECT_LABELS;\r\n\r\n protected readonly bottomTab = signal<DpeBottomTab>('diagnostics');\r\n\r\n private readonly canvas = viewChild(DpeCanvasComponent);\r\n\r\n /**\r\n * Il dialetto **della definizione**: e' quello con cui il backend compilera' (ADR 0009), quindi\r\n * e' quello che il badge mostra. In sola lettura: nessun comando dell'interfaccia lo cambia\r\n * (ADR 0017), perche' il database di destinazione e' un fatto dell'ambiente e non una\r\n * preferenza dell'autore.\r\n */\r\n protected readonly dialect = computed(() => this.store.definition().dialect);\r\n\r\n /**\r\n * Disallineamento fra cio' che l'ambiente dichiara e cio' che la definizione porta. Si\r\n * **segnala** e non si corregge: correggere vorrebbe dire cambiare le capacita' con cui la\r\n * definizione verra' compilata, che e' dell'host. Assente `getEnvironment`, non c'e' niente da\r\n * confrontare.\r\n */\r\n protected readonly dialectMismatch = computed<SqlDialectKind | undefined>(() => {\r\n const declared = this.validation.environmentDialect();\r\n return declared !== undefined && declared !== this.dialect() ? declared : undefined;\r\n });\r\n\r\n /**\r\n * Il documento porta una chiave utilizzabile. Il nome **e'** la chiave del catalogo, e la rotta di\r\n * aggiornamento e' `PUT /definitions/{name}`: senza nome non c'e' niente da aggiornare, e una\r\n * definizione nuova si crea dal catalogo (ADR 0019), non da qui.\r\n *\r\n * Sta qui e non solo nel client per la ragione che questo progetto applica alle primitive\r\n * assenti: un comando disabilitato che dice il motivo e' meglio di un comando attivo che fallisce\r\n * alla pressione. Il client rifiuta comunque — e' lui che compone l'URL, e un ospite che chiami\r\n * `save()` per vie proprie deve trovare la stessa risposta.\r\n */\r\n protected readonly hasKey = computed(() => (this.store.definition().name ?? '').trim().length > 0);\r\n\r\n /**\r\n * Il documento ricevuto **non e' una definizione**, detto subito e a schermo.\r\n *\r\n * L'unico ingresso e' l'input `definition`, e finora l'editor accettava qualunque oggetto: con un\r\n * documento della forma sbagliata mostrava un canvas vuoto — che sembra una definizione senza\r\n * nodi, cioe' uno stato legittimo — e il difetto ricompariva molto piu' tardi come un salvataggio\r\n * verso una chiave che il client doveva inventare. Le tre forme che arrivano davvero al posto del\r\n * documento sono l'involucro di lettura (`{ definition, diagnostics }`), il **sommario** del\r\n * catalogo (ha `name` ma non `formatVersion` ne' i nodi) e una risposta in PascalCase.\r\n *\r\n * Si controllano le due proprieta' obbligatorie e nient'altro: una definizione senza nodi e'\r\n * legittima, e verificare di piu' sarebbe validare — che e' del backend (I6).\r\n */\r\n protected readonly documentProblem = computed<string | undefined>(() => {\r\n const document = this.store.definition() as Partial<DpeDefinition> | null | undefined;\r\n if (typeof document?.formatVersion !== 'number') {\r\n return 'manca `formatVersion`';\r\n }\r\n if (!this.hasKey()) {\r\n return \"manca `name`, che e' la chiave del catalogo\";\r\n }\r\n return undefined;\r\n });\r\n\r\n protected readonly canSave = computed(\r\n () =>\r\n this.validation.isSaveAvailable() &&\r\n !this.validation.isSaving() &&\r\n this.store.isDirty() &&\r\n this.hasKey(),\r\n );\r\n\r\n protected readonly nodeCount = computed(() => dpeAllNodes(this.store.definition()).length);\r\n\r\n /** I nomi selezionati: la striscia contestuale compare da due in su. */\r\n protected readonly selectedNames = computed(() => this.store.selectedNodeNames());\r\n\r\n constructor() {\r\n // Caricamento: si reagisce all'**identita'** dell'input, non al suo contenuto. Lo store\r\n // diventa la fonte di verita' da qui in poi.\r\n effect(() => {\r\n const incoming = this.definition();\r\n // `untracked` non e' prudenza: senza, questo effect **si richiama da solo**. Legge il\r\n // documento dello store (attraverso `load`, e a maggior ragione se un domani si leggesse\r\n // qualcosa dello store qui) e lo riscrive, quindi ogni scrittura successiva — anche quella\r\n // del layout automatico — lo farebbe ripartire e riportare il documento all'input,\r\n // cancellando il layout appena calcolato e ricominciando. Il sintomo non e' un errore: e'\r\n // una pagina che non si ferma piu' e uno schermo che sembra solo lento.\r\n untracked(() => this.store.load(incoming));\r\n });\r\n\r\n // Un documento senza coordinate (ADR 0013: sono opzionali) si dispone da solo, una volta.\r\n // Farlo a ogni cambio combatterebbe con chi sposta i nodi a mano.\r\n effect(() => {\r\n const nodes = dpeAllNodes(this.store.definition()).map((located) => located.node);\r\n if (nodes.length === 0 || dpeHasCompleteLayout(nodes)) {\r\n return;\r\n }\r\n // Il calcolo legge il grafo e le misure delle card: fuori da `untracked` diventerebbero\r\n // dipendenze di questo effect, che scrive proprio cio' che legge.\r\n untracked(() => this.runLayout(true));\r\n });\r\n\r\n // La definizione esce verso l'ospite a ogni mutazione: e' lui che sa se e quando salvare.\r\n effect(() => {\r\n this.definitionChange.emit(this.store.definition());\r\n });\r\n }\r\n\r\n /** Comando «Riordina»: e' un'azione dell'utente, quindi annullabile e \"modifica\". */\r\n protected runAutoLayout(): void {\r\n this.runLayout(false);\r\n }\r\n\r\n /**\r\n * `isInitial` distingue due cose che dagre calcola allo stesso modo e che l'editor deve trattare\r\n * in modo opposto: disporre un documento che **non porta** coordinate non e' una modifica\r\n * dell'utente; riordinare su richiesta lo e'.\r\n */\r\n private runLayout(isInitial: boolean): void {\r\n const canvas = this.canvas();\r\n if (!canvas) {\r\n return;\r\n }\r\n const positions = this.layout.layout(\r\n canvas.layoutInput(),\r\n this.store.graph().edges.map((edge) => ({\r\n from: edge.fromNodeName,\r\n to: edge.toNodeName,\r\n id: edge.id,\r\n })),\r\n );\r\n if (isInitial) {\r\n this.store.applyInitialPositions(positions);\r\n return;\r\n }\r\n this.store.applyPositions(positions);\r\n }\r\n\r\n protected onNodeDropped(event: { collection: DpeNodeCollectionName; x: number; y: number }): void {\r\n const name = this.store.createNodeAt(event.collection, { x: event.x, y: event.y });\r\n // Selezione immediata: chi ha appena creato un nodo lo deve configurare, e cercarlo con il\r\n // mouse subito dopo averlo rilasciato e' un passaggio inutile.\r\n this.store.select(name);\r\n }\r\n\r\n /**\r\n * Doppio clic sul nodo, o bottone `✎` sulla card: i due gesti che il canvas emetteva su\r\n * `nodeOpened` e che **nessuno ascoltava** — il bottone non produceva nemmeno la selezione.\r\n *\r\n * Due cose, in quest'ordine. Si apre la dialog (che collassa la selezione a quel nodo) e si\r\n * **inquadra** il nodo: sotto il velo non si vede, ma alla chiusura si torna guardando il nodo su\r\n * cui si e' lavorato invece del punto dov'era il grafo prima.\r\n */\r\n protected onNodeOpened(name: string): void {\r\n this.store.openNodeEditor(name);\r\n this.canvas()?.revealNode(name);\r\n }\r\n\r\n /**\r\n * Elimina i nodi selezionati (striscia della selezione multipla). Si itera su una **copia** dei\r\n * nomi: `removeNode` pota la selezione a ogni giro, e scorrere il signal mentre lo si svuota\r\n * salterebbe la meta' dei nodi.\r\n */\r\n protected removeSelectedNodes(): void {\r\n for (const name of [...this.selectedNames()]) {\r\n this.store.removeNode(name);\r\n }\r\n }\r\n\r\n /** Clic sulla palette (via tastiera compresa): il nodo nasce in un punto libero e prevedibile. */\r\n protected onNodeRequested(collection: DpeNodeCollectionName): void {\r\n const existing = this.store.positions();\r\n let y = 60;\r\n for (const position of existing.values()) {\r\n y = Math.max(y, position.y + 140);\r\n }\r\n const name = this.store.createNodeAt(collection, { x: 80, y });\r\n this.store.select(name);\r\n }\r\n\r\n /**\r\n * `Ctrl+S` (e `Cmd+S`): stesse condizioni di abilitazione del bottone. `preventDefault` sempre\r\n * che la scorciatoia sia nostra, anche quando il salvataggio non e' possibile: lasciare passare\r\n * il «salva pagina» del browser proprio dentro un editor sarebbe la risposta sbagliata a un\r\n * gesto giusto.\r\n */\r\n protected onKeyDown(event: KeyboardEvent): void {\r\n if (!(event.ctrlKey || event.metaKey) || event.key.toLowerCase() !== 's') {\r\n return;\r\n }\r\n event.preventDefault();\r\n if (this.canSave()) {\r\n this.validation.save();\r\n }\r\n }\r\n\r\n /**\r\n * Navigazione da una diagnostica: **selezione e vista**. Il `DiagnosticPath` promette di portare\r\n * sul nodo e sul campo; selezionare senza inquadrare mantiene meta' della promessa, e il nodo\r\n * resta fuori schermo mentre l'ispettore parla di lui.\r\n */\r\n protected onNavigate(event: { nodeName: string }): void {\r\n this.store.select(event.nodeName);\r\n // Si muove la **vista**, non il documento: le coordinate del nodo non si toccano.\r\n this.canvas()?.revealNode(event.nodeName);\r\n }\r\n\r\n /**\r\n * Stato **sporco** del documento, per l'ospite che deve decidere se lasciar navigare via\r\n * (ADR 0019). Delega a `DpeDocumentStore.isDirty`.\r\n *\r\n * Si legge tramite `viewChild` **quando serve**, non si rispecchia in uno stato dell'ospite: un\r\n * mirror di stato sporco che va fuori sincrono e' peggio del problema che risolve. La conferma\r\n * prima di abbandonare le modifiche e' dell'ospite, perche' e' l'ospite che naviga — la libreria\r\n * non sa se andare via significhi cambiare schermata, scheda o applicazione.\r\n */\r\n readonly isDirty: Signal<boolean> = this.store.isDirty;\r\n\r\n /**\r\n * Da chiamare dopo un salvataggio riuscito: da quel momento il documento risulta \"senza\r\n * modifiche\". Lo decide l'ospite, perche' solo lui sa se il salvataggio e' andato a buon fine.\r\n */\r\n markSaved(): void {\r\n this.store.markSaved();\r\n }\r\n}\r\n","<!--\r\n `.dpe-builder` porta le variabili del tema: e’ la classe radice su cui l’app ospite le\r\n sovrascrive, e `data-dpe-theme=\"dark\"` sul contenitore ridefinisce i soli token.\r\n-->\r\n<div class=\"dpe-builder dpe-shell\">\r\n <header class=\"dpe-shell__bar\">\r\n <div class=\"dpe-shell__title\">\r\n <strong>{{ store.definition().label || store.definition().name || 'Definizione DPE' }}</strong>\r\n <span class=\"dpe-shell__sub\">{{ nodeCount() }} nodi</span>\r\n <!--\r\n Stato **sporco** rispetto all’ultimo salvataggio, non un conteggio di operazioni: serve a\r\n decidere se salvare, e un contatore che sale quando si annulla risponde alla domanda\r\n sbagliata. Annullare fino allo stato di partenza torna qui a «nessuna modifica».\r\n -->\r\n <span\r\n class=\"dpe-shell__dirty\"\r\n [class.dpe-shell__dirty--on]=\"store.isDirty()\"\r\n [title]=\"\r\n store.isDirty()\r\n ? 'Il documento differisce dall’ultimo salvataggio'\r\n : 'Il documento coincide con l’ultimo salvataggio'\r\n \"\r\n >\r\n @if (store.isDirty()) {\r\n ● modificata\r\n } @else {\r\n ○ nessuna modifica\r\n }\r\n </span>\r\n </div>\r\n\r\n <!--\r\n Badge in **sola lettura** (ADR 0017): il dialetto e’ dichiarato dall’ambiente, non scelto\r\n qui. Mostra quello della definizione, che e’ quello con cui il backend compilera’ (ADR\r\n 0009). Il titolo dice le due cose che contano — chi lo decide, e che il dialetto decide con\r\n quali capacita’ si compila. Non dice piu’ che le espressioni sono SQL di quel dialetto: da\r\n quando sono **formule** (ADR 0031) non lo sono, e il linguaggio si nomina accanto al campo.\r\n -->\r\n <span\r\n class=\"dpe-shell__dialect\"\r\n title=\"Il dialetto e’ dichiarato dall’ambiente e non si modifica dall’editor: decide con quali capacita’ il backend compilera’ questa definizione.\"\r\n >\r\n Dialetto · <strong>{{ dialectLabels[dialect()] }}</strong>\r\n </span>\r\n\r\n <div class=\"dpe-shell__group\">\r\n <button\r\n type=\"button\"\r\n class=\"dpe-shell__btn\"\r\n [disabled]=\"!store.canUndo()\"\r\n title=\"Annulla (la cronologia e’ una conseguenza dell’immutabilita’ dello stato)\"\r\n (click)=\"store.undo()\"\r\n >\r\n ↶ Annulla\r\n </button>\r\n <button\r\n type=\"button\"\r\n class=\"dpe-shell__btn\"\r\n [disabled]=\"!store.canRedo()\"\r\n title=\"Ripeti\"\r\n (click)=\"store.redo()\"\r\n >\r\n ↷ Ripeti\r\n </button>\r\n <button type=\"button\" class=\"dpe-shell__btn\" title=\"Riordina il grafo\" (click)=\"runAutoLayout()\">\r\n ⌗ Riordina\r\n </button>\r\n </div>\r\n\r\n <!--\r\n Salvataggio attraverso il contratto (`DpeBuilderApi.save`, opzionale). E’ il comando\r\n **primario**, separato dal gruppo annulla/ripeti/riordina: quelli disfano, questo scrive.\r\n\r\n **Sempre presente, mai nascosto.** Un bottone assente dice «questo editor non salva»; un\r\n bottone disabilitato con il motivo dice «qui non si salva, ed ecco perche’» — ed e’ la\r\n verita’ quando l’host non espone `save()`. La disponibilita’ si sa **prima del clic**\r\n (`core/dpe-capabilities.ts`), non dal primo fallimento.\r\n -->\r\n <div class=\"dpe-shell__group dpe-shell__group--primary\">\r\n <button\r\n type=\"button\"\r\n class=\"dpe-shell__btn dpe-shell__btn--primary\"\r\n [disabled]=\"!canSave()\"\r\n [title]=\"\r\n !validation.isSaveAvailable()\r\n ? 'Salvataggio non disponibile su questo ambiente: l’host non espone save()'\r\n : validation.isSaving()\r\n ? 'Salvataggio in corso'\r\n : !hasKey()\r\n ? 'Il documento non ha un nome: e’ la chiave del catalogo, e va creato da la’ (l’aggiornamento e’ PUT /definitions/{nome})'\r\n : store.isDirty()\r\n ? 'Salva la definizione (Ctrl+S)'\r\n : 'Nessuna modifica da salvare'\r\n \"\r\n (click)=\"validation.save()\"\r\n >\r\n @if (validation.isSaving()) {\r\n ⤓ Salvataggio…\r\n } @else {\r\n ⤓ Salva\r\n }\r\n </button>\r\n @if (validation.justSaved()) {\r\n <!-- Avviso transitorio: il fatto durevole e’ «nessuna modifica» nell’indicatore. -->\r\n <span class=\"dpe-shell__toast\" role=\"status\">✓ Salvata</span>\r\n }\r\n </div>\r\n\r\n <div class=\"dpe-shell__status\">\r\n @if (validation.isValidating()) {\r\n <span class=\"dpe-shell__pill dpe-shell__pill--busy\">Validazione in corso…</span>\r\n } @else if (validation.error(); as error) {\r\n <!--\r\n Un guasto di trasporto non cancella le diagnostiche precedenti: dire «va tutto bene»\r\n quando non lo sappiamo piu’ e’ peggio che mostrare un risultato vecchio dichiarandolo.\r\n -->\r\n <span class=\"dpe-shell__pill dpe-shell__pill--error\" [title]=\"error.message\">\r\n Backend non raggiungibile ({{ error.category }})\r\n </span>\r\n } @else if (store.validateResult()) {\r\n <span\r\n class=\"dpe-shell__pill\"\r\n [class.dpe-shell__pill--error]=\"store.validateResult()!.hasErrors\"\r\n [class.dpe-shell__pill--ok]=\"!store.validateResult()!.hasErrors\"\r\n >\r\n @if (store.validateResult()!.hasErrors) {\r\n Con errori\r\n } @else {\r\n Nessun errore\r\n }\r\n </span>\r\n }\r\n </div>\r\n </header>\r\n\r\n @if (dialectMismatch(); as declared) {\r\n <!--\r\n Disallineamento fra la dichiarazione dell’ambiente e la definizione (ADR 0017). Si nominano\r\n **entrambi** i dialetti e non si offre di correggere: cambiare dialetto cambia le capacita’\r\n con cui si compila e i dialetti che il compilatore di formule copre (ADR 0031 §5), ed e’\r\n dell’host. Non e’ una regola di dominio duplicata — non giudica la definizione, riporta il\r\n disaccordo fra due fatti che l’ospite ha fornito.\r\n -->\r\n <div class=\"dpe-shell__warn\" role=\"alert\">\r\n <strong>\r\n L’ambiente dichiara {{ dialectLabels[declared] }}, la definizione e’ scritta per\r\n {{ dialectLabels[dialect()] }}.\r\n </strong>\r\n <p>\r\n Il backend compilera’ con il dialetto <strong>della definizione</strong>. Allinearli cambia\r\n le capacita’ con cui si compila, e non si fa da qui.\r\n </p>\r\n </div>\r\n }\r\n\r\n <!--\r\n Il documento ricevuto non e' una definizione. Banner **persistente** e in cima: e' un difetto\r\n dell'integrazione, non dell'utente, e un canvas vuoto da solo non lo dice — sembra una\r\n definizione senza nodi. Non si nasconde l'editor: cio' che c'e' resta visibile, e il salvataggio\r\n e' disabilitato da se' perche' la chiave manca.\r\n -->\r\n @if (documentProblem(); as problem) {\r\n <div class=\"dpe-shell__warn dpe-shell__warn--error\" role=\"alert\">\r\n <strong>Il documento ricevuto non e’ una definizione: {{ problem }}.</strong>\r\n <p>\r\n L’editor riceve il documento dall’applicazione ospite, e questo non ha la forma di una\r\n <code>DpeDefinition</code>. Le tre cause reali: il backend restituisce l’involucro di lettura\r\n (<code>{{ '{' }} definition, diagnostics {{ '}' }}</code>) invece del documento; l’ospite\r\n passa il <strong>sommario</strong> del catalogo al posto della definizione caricata con\r\n <code>load(nome)</code>; la risposta e’ in PascalCase (<code>Name</code> non e’\r\n <code>name</code>).\r\n </p>\r\n </div>\r\n }\r\n\r\n @if (validation.saveError(); as error) {\r\n <!--\r\n Banner **persistente**: un salvataggio fallito in silenzio e’ peggio di nessun salvataggio,\r\n perche’ l’indicatore «modificata» sparirebbe per un’operazione che non e’ avvenuta. Qui non\r\n sparisce: `markSaved()` viene chiamato solo al successo.\r\n -->\r\n <div class=\"dpe-shell__warn dpe-shell__warn--error\" role=\"alert\">\r\n <strong>Salvataggio non riuscito ({{ error.category }})</strong>\r\n <p>{{ error.message }}</p>\r\n <div class=\"dpe-shell__group\">\r\n <button type=\"button\" class=\"dpe-shell__btn\" (click)=\"validation.dismissSaveError()\">\r\n Ho capito\r\n </button>\r\n </div>\r\n </div>\r\n }\r\n\r\n @if (selectedNames().length > 1) {\r\n <!--\r\n Striscia contestuale della **selezione multipla**. Sta qui, fra i banner di documento e il\r\n corpo, perche' e' un fatto che riguarda l’insieme e non un nodo — ed e' dove l’occhio guarda\r\n per i fatti dell’insieme. Non mostra i campi di un nodo: mostrarli mentre N sono evidenziati\r\n e' la stessa bugia della doppia evidenziazione, detta piu' piano.\r\n -->\r\n <div class=\"dpe-shell__multi\">\r\n <div class=\"dpe-shell__multi-head\">\r\n <strong>{{ selectedNames().length }} nodi selezionati</strong>\r\n <ul class=\"dpe-multi__names\">\r\n @for (name of selectedNames(); track $index) {\r\n <li class=\"dpe-multi__name\">{{ name }}</li>\r\n }\r\n </ul>\r\n </div>\r\n\r\n <div class=\"dpe-shell__group\">\r\n <button type=\"button\" class=\"dpe-shell__btn\" (click)=\"removeSelectedNodes()\">\r\n Elimina i {{ selectedNames().length }} nodi\r\n </button>\r\n <!--\r\n Il riordino e’ dichiaratamente **dell’intero grafo**: dagre su un sottografo sarebbe una\r\n funzione nuova, e chiamarla «riordina i selezionati» mentre riordina tutto sarebbe una\r\n bugia — la stessa classe di difetto che questa striscia esiste per evitare.\r\n -->\r\n <button type=\"button\" class=\"dpe-shell__btn\" (click)=\"runAutoLayout()\">\r\n Riordina il grafo\r\n </button>\r\n </div>\r\n\r\n <p class=\"dpe-note dpe-note--warning\">\r\n L’eliminazione lascia i riferimenti a valle così come sono: diventano riferimenti rotti, e\r\n il backend li segnala. Si annulla con ↶.\r\n </p>\r\n </div>\r\n }\r\n\r\n <div class=\"dpe-shell__body\">\r\n <dpe-node-palette class=\"dpe-shell__palette\" (nodeRequested)=\"onNodeRequested($event)\" />\r\n\r\n <dpe-canvas\r\n class=\"dpe-shell__canvas\"\r\n [isEditable]=\"isEditable()\"\r\n (nodeDropped)=\"onNodeDropped($event)\"\r\n (nodeOpened)=\"onNodeOpened($event)\"\r\n />\r\n </div>\r\n\r\n <!--\r\n **Una sola istanza**, fratello di `.dpe-shell__body`: non dentro il canvas (finirebbe nella\r\n trasformazione CSS di `f-canvas`) e non dentro il corpo (partecipererebbe alla griglia). Non\r\n partecipa al layout: `showModal()` la promuove nel top layer, quindi aprirla non produce\r\n nessun reflow del canvas e non c'e' nessuna inquadratura da rifare.\r\n -->\r\n <dpe-node-dialog />\r\n\r\n <div class=\"dpe-shell__bottom\">\r\n <div class=\"dpe-shell__tabs\" role=\"tablist\" aria-label=\"Pannelli\">\r\n <button\r\n type=\"button\"\r\n role=\"tab\"\r\n class=\"dpe-shell__tab\"\r\n [class.dpe-shell__tab--active]=\"bottomTab() === 'diagnostics'\"\r\n [attr.aria-selected]=\"bottomTab() === 'diagnostics'\"\r\n (click)=\"bottomTab.set('diagnostics')\"\r\n >\r\n Diagnostiche\r\n </button>\r\n <button\r\n type=\"button\"\r\n role=\"tab\"\r\n class=\"dpe-shell__tab\"\r\n [class.dpe-shell__tab--active]=\"bottomTab() === 'schema'\"\r\n [attr.aria-selected]=\"bottomTab() === 'schema'\"\r\n (click)=\"bottomTab.set('schema')\"\r\n >\r\n Schema\r\n </button>\r\n <button\r\n type=\"button\"\r\n role=\"tab\"\r\n class=\"dpe-shell__tab\"\r\n [class.dpe-shell__tab--active]=\"bottomTab() === 'sql'\"\r\n [attr.aria-selected]=\"bottomTab() === 'sql'\"\r\n (click)=\"bottomTab.set('sql')\"\r\n >\r\n SQL generato\r\n </button>\r\n </div>\r\n\r\n @switch (bottomTab()) {\r\n @case ('diagnostics') {\r\n <dpe-diagnostics-panel class=\"dpe-shell__panel\" (navigate)=\"onNavigate($event)\" />\r\n }\r\n @case ('schema') {\r\n <dpe-schema-panel class=\"dpe-shell__panel\" />\r\n }\r\n @case ('sql') {\r\n <dpe-sql-preview class=\"dpe-shell__panel\" />\r\n }\r\n }\r\n </div>\r\n</div>\r\n","/**\r\n * Il catalogo delle definizioni: `<dpe-catalog>` (ADR 0019).\r\n *\r\n * **E' fratello dell'editor, non suo genitore.** `DpeBuilderComponent` non lo importa, non lo\r\n * conosce e non lo monta: `definition` resta `input.required`, l'ospite che vuole solo l'editor\r\n * non paga nulla, e due editor nella stessa pagina non diventano due cataloghi affiancati.\r\n *\r\n * Cosa fa: elenca, filtra in locale, crea, duplica, cancella. Cosa **non** fa: aprire. Emette il\r\n * **nome** con `open` e si ferma li', perche' chi apre puo' voler instradare, controllare permessi\r\n * o aprire in un'altra scheda — decisioni dell'ospite, non nostre.\r\n *\r\n * Provider dello store a livello di **componente**, come per l'editor: due cataloghi in due punti\r\n * dell'ospite sono due elenchi indipendenti.\r\n */\r\n\r\nimport {\r\n ChangeDetectionStrategy,\r\n Component,\r\n ElementRef,\r\n Injector,\r\n afterNextRender,\r\n computed,\r\n inject,\r\n output,\r\n signal,\r\n viewChild,\r\n} from '@angular/core';\r\n\r\nimport { DpeCatalogStore } from '../../core/dpe-catalog.store';\r\nimport { DPE_DIALECT_LABELS } from '../../core/dpe-node-catalog';\r\nimport { DpeDefinitionSummary } from '../../model/dpe-api.model';\r\n\r\n@Component({\r\n selector: 'dpe-catalog',\r\n standalone: true,\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n providers: [DpeCatalogStore],\r\n templateUrl: './dpe-catalog.component.html',\r\n})\r\nexport class DpeCatalogComponent {\r\n protected readonly store = inject(DpeCatalogStore);\r\n private readonly injector = inject(Injector);\r\n\r\n /**\r\n * Il **nome** della definizione da aprire. Non la definizione: caricarla e' dell'ospite, che\r\n * potrebbe volerlo fare per vie proprie.\r\n */\r\n readonly open = output<string>();\r\n\r\n protected readonly dialectLabels = DPE_DIALECT_LABELS;\r\n\r\n /** Filtro **locale** sui sommari gia' ricevuti: non e' una query al backend (ADR 0019). */\r\n protected readonly filter = signal('');\r\n\r\n protected readonly newName = signal('');\r\n protected readonly newLabel = signal('');\r\n\r\n /**\r\n * Conferma in **due passi**: un elenco in cui un clic distratto cancella non e' un elenco.\r\n * Tiene il nome invece di un booleano perche' la conferma appartiene a **quella** riga.\r\n */\r\n protected readonly pendingDelete = signal<string | null>(null);\r\n\r\n /** Riga in duplicazione, con i due campi del nome e dell'etichetta nuovi. */\r\n protected readonly duplicateSource = signal<string | null>(null);\r\n protected readonly duplicateName = signal('');\r\n protected readonly duplicateLabel = signal('');\r\n\r\n private readonly duplicateNameField =\r\n viewChild<ElementRef<HTMLInputElement>>('duplicateNameField');\r\n\r\n /**\r\n * Il filtro non tocca l'ordine: si tolgono righe, non si riordinano. `toLocaleLowerCase` e non\r\n * `toLowerCase` perche' un elenco di definizioni italiane si filtra anche per lettere accentate.\r\n */\r\n protected readonly visible = computed<readonly DpeDefinitionSummary[]>(() => {\r\n const needle = this.filter().trim().toLocaleLowerCase();\r\n if (needle.length === 0) {\r\n return this.store.summaries();\r\n }\r\n return this.store\r\n .summaries()\r\n .filter(\r\n (summary) =>\r\n summary.name.toLocaleLowerCase().includes(needle) ||\r\n summary.label.toLocaleLowerCase().includes(needle),\r\n );\r\n });\r\n\r\n /** Vero quando il filtro ha nascosto tutto: e' un messaggio diverso da «nessuna definizione». */\r\n protected readonly isFilteredEmpty = computed(\r\n () => this.store.summaries().length > 0 && this.visible().length === 0,\r\n );\r\n\r\n /**\r\n * Duplicare richiede **due** primitive, `load` e `createDefinition`: non esiste una primitiva\r\n * di duplicazione, il comando si compone da queste.\r\n */\r\n protected readonly canDuplicate = computed(\r\n () => this.store.capabilities.load && this.store.capabilities.createDefinition,\r\n );\r\n\r\n protected readonly duplicateReason = computed(() =>\r\n this.canDuplicate()\r\n ? 'Duplica: carica questa definizione e la ricrea con un nome nuovo'\r\n : 'Duplicazione non disponibile: questo ambiente non implementa load e createDefinition.',\r\n );\r\n\r\n /** Validazione **di forma**, l'unica ammessa nel frontend: due campi non vuoti. */\r\n protected readonly canSubmitNew = computed(\r\n () =>\r\n this.store.createCommand.isAvailable &&\r\n !this.store.isMutating() &&\r\n this.newName().trim().length > 0 &&\r\n this.newLabel().trim().length > 0,\r\n );\r\n\r\n protected readonly canSubmitDuplicate = computed(\r\n () =>\r\n this.canDuplicate() &&\r\n !this.store.isMutating() &&\r\n this.duplicateName().trim().length > 0 &&\r\n this.duplicateLabel().trim().length > 0,\r\n );\r\n\r\n protected onOpen(name: string): void {\r\n this.open.emit(name);\r\n }\r\n\r\n protected refresh(): void {\r\n this.pendingDelete.set(null);\r\n this.store.refresh();\r\n }\r\n\r\n /**\r\n * Crea e **apre il nome tornato dall'ospite**, non quello digitato: l'ospite puo' averlo\r\n * normalizzato, e aprire quello digitato aprirebbe una definizione che non esiste.\r\n */\r\n protected createNew(): void {\r\n if (!this.canSubmitNew()) {\r\n return;\r\n }\r\n const dialect = this.store.environmentDialect();\r\n if (dialect === undefined) {\r\n // Non dovrebbe accadere: `createCommand` lo esige. Se accade, non si ripiega su un\r\n // dialetto qualsiasi — sarebbe una definizione per il database sbagliato.\r\n return;\r\n }\r\n this.store\r\n .create(this.newName().trim(), this.newLabel().trim(), dialect)\r\n .subscribe((summary) => {\r\n this.newName.set('');\r\n this.newLabel.set('');\r\n this.open.emit(summary.name);\r\n });\r\n }\r\n\r\n protected startDuplicate(summary: DpeDefinitionSummary): void {\r\n this.pendingDelete.set(null);\r\n this.duplicateSource.set(summary.name);\r\n this.duplicateName.set(`${summary.name}Copia`);\r\n this.duplicateLabel.set(`${summary.label} (copia)`);\r\n // `afterNextRender` e non `setTimeout`: il rinvio al task successivo scatta **prima** del\r\n // refresh della view, la query e' ancora vuota e il focus non arriva — senza nessun errore.\r\n afterNextRender(() => this.duplicateNameField()?.nativeElement.focus(), {\r\n injector: this.injector,\r\n });\r\n }\r\n\r\n protected cancelDuplicate(): void {\r\n this.duplicateSource.set(null);\r\n }\r\n\r\n protected confirmDuplicate(): void {\r\n const source = this.duplicateSource();\r\n if (source === null || !this.canSubmitDuplicate()) {\r\n return;\r\n }\r\n this.store\r\n .duplicate(source, this.duplicateName().trim(), this.duplicateLabel().trim())\r\n .subscribe(() => {\r\n // Si resta nell'elenco: la riga nuova che compare **e'** la conferma. Aprire la copia\r\n // porterebbe via dall'elenco proprio chi sta riorganizzando il catalogo.\r\n this.duplicateSource.set(null);\r\n });\r\n }\r\n\r\n protected askDelete(name: string): void {\r\n this.duplicateSource.set(null);\r\n this.pendingDelete.set(name);\r\n }\r\n\r\n protected cancelDelete(): void {\r\n this.pendingDelete.set(null);\r\n }\r\n\r\n protected confirmDelete(name: string): void {\r\n this.pendingDelete.set(null);\r\n this.store.remove(name);\r\n }\r\n\r\n /** Solo la data, senza ora: un elenco non e' un registro di audit. */\r\n protected formatDate(value: string): string {\r\n const parsed = new Date(value);\r\n return Number.isNaN(parsed.getTime()) ? value : parsed.toLocaleDateString();\r\n }\r\n}\r\n","<!--\r\n `.dpe-theme` porta le variabili del tema anche fuori dall’editor: il catalogo e’ un componente\r\n **fratello**, quindi non puo’ contare sulla classe radice di `<dpe-builder>`. La variante scura\r\n resta quella prevista, `data-dpe-theme=\"dark\"` su un contenitore dell’ospite.\r\n-->\r\n<section class=\"dpe-theme dpe-cat\" aria-labelledby=\"dpe-cat-title\">\r\n <header class=\"dpe-cat__bar\">\r\n <h2 class=\"dpe-cat__title\" id=\"dpe-cat-title\">Definizioni DPE</h2>\r\n\r\n <span class=\"dpe-cat__count\">\r\n @if (store.isLoading()) {\r\n caricamento…\r\n } @else if (!store.capabilities.listDefinitions) {\r\n elenco non disponibile\r\n } @else {\r\n {{ visible().length }} di {{ store.summaries().length }}\r\n }\r\n </span>\r\n\r\n <input\r\n class=\"dpe-cat__filter\"\r\n type=\"search\"\r\n placeholder=\"Filtra per nome o etichetta\"\r\n aria-label=\"Filtra le definizioni\"\r\n [value]=\"filter()\"\r\n (input)=\"filter.set($any($event.target).value)\"\r\n />\r\n\r\n <button\r\n type=\"button\"\r\n class=\"dpe-cat__btn\"\r\n [disabled]=\"!store.capabilities.listDefinitions || store.isLoading()\"\r\n [title]=\"\r\n store.capabilities.listDefinitions\r\n ? 'Ricarica l’elenco'\r\n : 'Questo ambiente non implementa listDefinitions.'\r\n \"\r\n (click)=\"refresh()\"\r\n >\r\n ⟳ Aggiorna\r\n </button>\r\n </header>\r\n\r\n @if (store.error(); as error) {\r\n <!--\r\n Banner persistente: un errore di catalogo che svanisce lascia un elenco vecchio senza dire\r\n che e’ vecchio. La categoria si mostra perche’ distingue «riprova» da «chiedi all’ospite».\r\n -->\r\n <p class=\"dpe-cat__error\" role=\"alert\">\r\n <strong>{{ error.category }}</strong> {{ error.message }}\r\n <button type=\"button\" class=\"dpe-cat__dismiss\" aria-label=\"Chiudi\" (click)=\"store.dismissError()\">\r\n ×\r\n </button>\r\n </p>\r\n }\r\n\r\n <ul class=\"dpe-cat__list\">\r\n @for (summary of visible(); track summary.name) {\r\n <li class=\"dpe-cat__item\" [class.dpe-cat__item--busy]=\"store.isMutating()\">\r\n <div class=\"dpe-cat__row\">\r\n <button\r\n type=\"button\"\r\n class=\"dpe-cat__open\"\r\n [title]=\"'Apri ' + summary.label\"\r\n (click)=\"onOpen(summary.name)\"\r\n >\r\n <span class=\"dpe-cat__label\">{{ summary.label }}</span>\r\n <span class=\"dpe-cat__meta\">\r\n <!--\r\n Solo i fatti **presenti**: cio’ che il sommario non porta non si stima. Contare i\r\n nodi lato client vorrebbe dire caricare tutte le definizioni per disegnare un\r\n elenco (ADR 0019).\r\n -->\r\n <code class=\"dpe-cat__name\">{{ summary.name }}</code>\r\n <span class=\"dpe-cat__dot\"> · </span>\r\n {{ dialectLabels[summary.dialect] }}\r\n @if (summary.status) {\r\n <span class=\"dpe-cat__dot\"> · </span>\r\n {{ summary.status === 'Active' ? 'attiva' : 'non attiva' }}\r\n }\r\n @if (summary.runMode) {\r\n <span class=\"dpe-cat__dot\"> · </span>\r\n {{ summary.runMode === 'Batch' ? 'batch' : 'a richiesta' }}\r\n }\r\n @if (summary.isTemplate) {\r\n <span class=\"dpe-cat__dot\"> · </span>\r\n modello\r\n }\r\n @if (summary.nodeCount !== undefined) {\r\n <span class=\"dpe-cat__dot\"> · </span>\r\n {{ summary.nodeCount }} {{ summary.nodeCount === 1 ? 'nodo' : 'nodi' }}\r\n }\r\n @if (summary.updatedAt) {\r\n <span class=\"dpe-cat__dot\"> · </span>\r\n {{ formatDate(summary.updatedAt) }}\r\n @if (summary.updatedBy) {\r\n · {{ summary.updatedBy }}\r\n }\r\n }\r\n </span>\r\n @if (summary.description) {\r\n <span class=\"dpe-cat__desc\">{{ summary.description }}</span>\r\n }\r\n </button>\r\n\r\n <!--\r\n `hasErrors` e’ **riportato, non ricalcolato** (ADR 0019): il frontend non valida\r\n niente. Per questo l’etichetta dice «ultima validazione nota» e non «invalida»: puo’\r\n essere vecchia, e spacciarla per verita’ sarebbe una validazione fatta dal client per\r\n interposta persona. Assente, non compare nulla.\r\n -->\r\n @if (summary.hasErrors !== undefined) {\r\n <span\r\n class=\"dpe-cat__flag\"\r\n [class.dpe-cat__flag--errors]=\"summary.hasErrors\"\r\n [title]=\"\r\n summary.hasErrors\r\n ? 'Ultima validazione nota all’ambiente: con errori. E’ uno stato riportato, non ricalcolato qui.'\r\n : 'Ultima validazione nota all’ambiente: senza errori. E’ uno stato riportato, non ricalcolato qui.'\r\n \"\r\n >\r\n {{ summary.hasErrors ? 'con errori (stato noto)' : 'senza errori (stato noto)' }}\r\n </span>\r\n }\r\n\r\n <div class=\"dpe-cat__actions\">\r\n <button\r\n type=\"button\"\r\n class=\"dpe-cat__btn\"\r\n [disabled]=\"!canDuplicate() || store.isMutating()\"\r\n [title]=\"duplicateReason()\"\r\n (click)=\"startDuplicate(summary)\"\r\n >\r\n Duplica\r\n </button>\r\n\r\n @if (pendingDelete() === summary.name) {\r\n <!-- Conferma in **due passi**: la prima pressione chiede, la seconda cancella. -->\r\n <span class=\"dpe-cat__confirm\">\r\n Eliminare «{{ summary.name }}»?\r\n <button\r\n type=\"button\"\r\n class=\"dpe-cat__btn dpe-cat__btn--danger\"\r\n (click)=\"confirmDelete(summary.name)\"\r\n >\r\n Elimina\r\n </button>\r\n <button type=\"button\" class=\"dpe-cat__btn\" (click)=\"cancelDelete()\">Annulla</button>\r\n </span>\r\n } @else {\r\n <button\r\n type=\"button\"\r\n class=\"dpe-cat__btn\"\r\n [disabled]=\"!store.capabilities.deleteDefinition || store.isMutating()\"\r\n [title]=\"\r\n store.capabilities.deleteDefinition\r\n ? 'Elimina (chiede conferma)'\r\n : 'Eliminazione non disponibile: questo ambiente non implementa deleteDefinition.'\r\n \"\r\n (click)=\"askDelete(summary.name)\"\r\n >\r\n Elimina\r\n </button>\r\n }\r\n </div>\r\n </div>\r\n\r\n @if (duplicateSource() === summary.name) {\r\n <!--\r\n Duplicare e’ `load` piu’ `createDefinition`: nessuna primitiva nuova sul contratto.\r\n Il dialetto resta quello della definizione copiata: cambiarlo copiando sarebbe una\r\n migrazione silenziosa delle capacita’ con cui la copia verra’ compilata.\r\n -->\r\n <div class=\"dpe-cat__form dpe-cat__form--inline\">\r\n <label class=\"dpe-cat__field\">\r\n <span>Nome della copia</span>\r\n <input\r\n #duplicateNameField\r\n class=\"dpe-cat__input\"\r\n [value]=\"duplicateName()\"\r\n (input)=\"duplicateName.set($any($event.target).value)\"\r\n />\r\n </label>\r\n <label class=\"dpe-cat__field\">\r\n <span>Etichetta della copia</span>\r\n <input\r\n class=\"dpe-cat__input\"\r\n [value]=\"duplicateLabel()\"\r\n (input)=\"duplicateLabel.set($any($event.target).value)\"\r\n />\r\n </label>\r\n <button\r\n type=\"button\"\r\n class=\"dpe-cat__btn dpe-cat__btn--primary\"\r\n [disabled]=\"!canSubmitDuplicate()\"\r\n (click)=\"confirmDuplicate()\"\r\n >\r\n Duplica\r\n </button>\r\n <button type=\"button\" class=\"dpe-cat__btn\" (click)=\"cancelDuplicate()\">Annulla</button>\r\n </div>\r\n }\r\n </li>\r\n } @empty {\r\n <li class=\"dpe-cat__empty\">\r\n @if (!store.capabilities.listDefinitions) {\r\n Questo ambiente non implementa <code>listDefinitions</code>: l’elenco lo fornisce\r\n l’applicazione ospite.\r\n } @else if (store.isLoading()) {\r\n Caricamento dell’elenco…\r\n } @else if (isFilteredEmpty()) {\r\n Nessuna definizione corrisponde al filtro.\r\n } @else if (store.hasLoaded()) {\r\n Nessuna definizione. Creane una qui sotto.\r\n } @else {\r\n Elenco non disponibile.\r\n }\r\n </li>\r\n }\r\n </ul>\r\n\r\n <footer class=\"dpe-cat__foot\">\r\n <div class=\"dpe-cat__form\">\r\n <label class=\"dpe-cat__field\">\r\n <span>Nome tecnico</span>\r\n <input\r\n class=\"dpe-cat__input\"\r\n placeholder=\"RiepilogoOrdini\"\r\n [value]=\"newName()\"\r\n [disabled]=\"!store.createCommand.isAvailable\"\r\n (input)=\"newName.set($any($event.target).value)\"\r\n />\r\n </label>\r\n <label class=\"dpe-cat__field\">\r\n <!--\r\n Si chiedono **due** campi e non uno: passare il nome anche come etichetta produce per\r\n sempre un catalogo di nomi tecnici, e un default sbagliato non si corregge piu’.\r\n -->\r\n <span>Etichetta</span>\r\n <input\r\n class=\"dpe-cat__input\"\r\n placeholder=\"Riepilogo ordini\"\r\n [value]=\"newLabel()\"\r\n [disabled]=\"!store.createCommand.isAvailable\"\r\n (input)=\"newLabel.set($any($event.target).value)\"\r\n />\r\n </label>\r\n <button\r\n type=\"button\"\r\n class=\"dpe-cat__btn dpe-cat__btn--primary\"\r\n [disabled]=\"!canSubmitNew()\"\r\n [title]=\"\r\n store.createCommand.reason ??\r\n 'Crea la definizione e la apre. Il dialetto viene dall’ambiente, non si sceglie qui.'\r\n \"\r\n (click)=\"createNew()\"\r\n >\r\n + Crea e apri\r\n </button>\r\n </div>\r\n\r\n <p class=\"dpe-cat__hint\">\r\n @if (store.createCommand.isAvailable) {\r\n <!--\r\n Detto **al momento della creazione**, non lasciato scoprire: una definizione nuova non\r\n ha sorgenti ne’ writeback, quindi l’editor la segnalera’ invalida. E’ normale — il\r\n modello tollera l’incompleto e la validazione lo racconta.\r\n -->\r\n Il dialetto della definizione nuova e’ quello dichiarato dall’ambiente\r\n @if (store.environmentDialect(); as declared) {\r\n (<strong>{{ dialectLabels[declared] }}</strong>)\r\n }\r\n e non si sceglie qui. Una definizione appena creata\r\n e’ <strong>invalida</strong> — servono almeno una sorgente e un writeback — ed e’ normale.\r\n } @else {\r\n {{ store.createCommand.reason }}\r\n }\r\n </p>\r\n </footer>\r\n</section>\r\n","/*\r\n * Superficie pubblica di `@esfaenza/dpe-builder`.\r\n *\r\n * Cio' che non e' esportato qui non e' API: chi integra la libreria usa questi tipi e nulla\r\n * di piu'. In questa tranche esistono contratto, modello e `core/`; i componenti dell'editor\r\n * arrivano nella successiva.\r\n */\r\n\r\n// Contratto verso il backend: il contratto **e'** la classe astratta (ADR 0007).\r\nexport * from './lib/api/dpe-builder-api';\r\nexport * from './lib/api/dpe-api-error';\r\n// Client HTTP di libreria (ADR 0020, che revisiona la collocazione decisa dall'ADR 0007). E'\r\n// **una** implementazione del contratto, non il contratto: chi non usa HTTP non la registra e\r\n// `sideEffects: false` la rende eliminabile dal bundle.\r\nexport * from './lib/api/http-dpe-builder-api';\r\n\r\n// Modello: allineato a mano ai record C#.\r\nexport * from './lib/model/dpe-definition.model';\r\nexport * from './lib/model/dpe-diagnostics.model';\r\nexport * from './lib/model/dpe-api.model';\r\n\r\n// Stato e utility pure.\r\nexport * from './lib/core/dpe-capabilities';\r\nexport * from './lib/core/dpe-catalog.store';\r\n// Le condizioni dei campi condizionali, in un punto solo: le legge la resa di §5-ter e le legge la\r\n// ripulitura. Esportate perche' chi scrive un ispettore suo deve leggere **queste**, non ricopiarle.\r\nexport * from './lib/core/dpe-conditional-fields';\r\nexport * from './lib/core/dpe-document.store';\r\n// La cache del catalogo delle formule (ADR 0036, `frontend.md` §5-quater): esportata perche' chi\r\n// scrive un ispettore suo deve passare da qui e non chiamare `getFormulaCatalog` per conto proprio.\r\nexport * from './lib/core/dpe-formula-catalog.store';\r\n// La lettura dei tre esiti del catalogo: pura, e la stessa per chiunque renda un campo formula.\r\n// Riscriverla in un ispettore dell'ospite e' il modo di far tornare il degrado silenzioso.\r\nexport * from './lib/core/dpe-formula-catalog.view';\r\nexport * from './lib/core/dpe-diagnostics.util';\r\nexport * from './lib/core/dpe-graph';\r\nexport * from './lib/core/dpe-layout.service';\r\nexport * from './lib/core/dpe-metadata.store';\r\nexport * from './lib/core/dpe-node-catalog';\r\nexport * from './lib/core/dpe-node-icons';\r\nexport * from './lib/core/dpe-node-factory';\r\nexport * from './lib/core/dpe-node-layout';\r\nexport * from './lib/core/dpe-validation.service';\r\nexport * from './lib/core/dpe-viewport';\r\n\r\n// UI: il componente radice e i pezzi montabili da soli.\r\nexport * from './lib/ui/dpe-builder.component';\r\nexport * from './lib/ui/canvas/dpe-canvas.component';\r\n// Il catalogo (ADR 0019) e' **fratello** dell'editor: esportato perche' l'ospite possa montarlo,\r\n// e non importato da `DpeBuilderComponent`. Chi vuole solo l'editor non lo paga.\r\nexport * from './lib/ui/catalog/dpe-catalog.component';\r\nexport * from './lib/ui/canvas/dpe-canvas.model';\r\nexport * from './lib/ui/palette/dpe-node-palette.component';\r\n// La dialog di modifica del nodo (ADR 0026) e i due componenti che ne riempiono le colonne.\r\n// `dpe-node-inspector` resta esportato ma **cambia forma**: solo i campi modificabili di un nodo.\r\nexport * from './lib/ui/dialog/dpe-node-dialog.component';\r\nexport * from './lib/ui/inspector/dpe-node-inspector.component';\r\nexport * from './lib/ui/inspector/dpe-node-facts.component';\r\nexport * from './lib/ui/inspector/dpe-node-inspector-base';\r\nexport * from './lib/ui/diagnostics/dpe-diagnostics-panel.component';\r\nexport * from './lib/ui/schema/dpe-schema-panel.component';\r\nexport * from './lib/ui/preview/dpe-sql-preview.component';\r\nexport * from './lib/ui/shared/dpe-expression-editor.component';\r\nexport * from './lib/ui/shared/dpe-column-type-editor.component';\r\nexport * from './lib/ui/shared/dpe-select-value.directive';\r\nexport * from './lib/ui/shared/dpe-node-icon.component';\r\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":["FUNCTIONS","KINDS","DELIMITERS"],"mappings":";;;;;;;;;AAAA;;;;;;;AAOG;AAgBG,MAAO,WAAY,SAAQ,KAAK,CAAA;AAEzB,IAAA,QAAA;AAGA,IAAA,OAAA;IAJX,WAAA,CACW,QAA6B,EACtC,OAAe;;IAEN,OAAiB,EAAA;QAE1B,KAAK,CAAC,OAAO,CAAC;QALL,IAAA,CAAA,QAAQ,GAAR,QAAQ;QAGR,IAAA,CAAA,OAAO,GAAP,OAAO;AAGhB,QAAA,IAAI,CAAC,IAAI,GAAG,aAAa;IAC3B;IAEA,OAAO,EAAE,CAAC,KAAc,EAAA;QACtB,OAAO,KAAK,YAAY,WAAW;IACrC;;IAGA,OAAO,gBAAgB,CAAC,KAAc,EAAA;AACpC,QAAA,OAAO,WAAW,CAAC,EAAE,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC,QAAQ,KAAK,gBAAgB;IACrE;AACD;AAED;AACO,MAAM,8BAA8B,GAAwC;AACjF,IAAA,cAAc,EAAE,8CAA8C;AAC9D,IAAA,SAAS,EAAE,kCAAkC;AAC7C,IAAA,OAAO,EAAE,uCAAuC;;;AChDlD;;;;;;;;;;;;;;;;;;;;;;;;;AAyBG;AAoBH;AACA,SAAS,OAAO,CAAC,SAAiB,EAAA;AAChC,IAAA,OAAO,UAAU,CACf,MAAM,IAAI,WAAW,CAAC,gBAAgB,EAAE,CAAA,8CAAA,EAAiD,SAAS,CAAA,CAAA,CAAG,CAAC,CACvG;AACH;MAEsB,aAAa,CAAA;AACjC;;;;;;;;;;;;;;;;;;AAkBG;IACM,qBAAqB,GAAoC,EAAE;AAkCpE;;;;;;;;;;AAUG;IACH,cAAc,CACZ,UAAyB,EACzB,eAAqC,EAAA;AAErC,QAAA,KAAK,UAAU;AACf,QAAA,KAAK,eAAe;AACpB,QAAA,OAAO,OAAO,CAAC,gBAAgB,CAAC;IAClC;AAEA;;;;;;AAMG;AACH,IAAA,IAAI,CAAC,IAAY,EAAA;AACf,QAAA,KAAK,IAAI;AACT,QAAA,OAAO,OAAO,CAAC,MAAM,CAAC;IACxB;;AAGA,IAAA,IAAI,CAAC,UAAyB,EAAA;AAC5B,QAAA,KAAK,UAAU;AACf,QAAA,OAAO,OAAO,CAAC,MAAM,CAAC;IACxB;AAEA;;;;;;;;;;;;;AAaG;IACH,cAAc,GAAA;AACZ,QAAA,OAAO,OAAO,CAAC,gBAAgB,CAAC;IAClC;AAEA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA+BG;IACH,iBAAiB,CACf,UAAyB,EACzB,QAAgB,EAAA;AAEhB,QAAA,KAAK,UAAU;AACf,QAAA,KAAK,QAAQ;AACb,QAAA,OAAO,OAAO,CAAC,mBAAmB,CAAC;IACrC;AAEA;;;;;;;;;;AAUG;IACH,eAAe,GAAA;AACb,QAAA,OAAO,OAAO,CAAC,iBAAiB,CAAC;IACnC;AAEA;;;;;;;;;;;;;AAaG;AACH,IAAA,gBAAgB,CAAC,UAAyB,EAAA;AACxC,QAAA,KAAK,UAAU;AACf,QAAA,OAAO,OAAO,CAAC,kBAAkB,CAAC;IACpC;AAEA;;;;;AAKG;AACH,IAAA,gBAAgB,CAAC,IAAY,EAAA;AAC3B,QAAA,KAAK,IAAI;AACT,QAAA,OAAO,OAAO,CAAC,kBAAkB,CAAC;IACpC;AACD;;ACvPD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAyCG;AA2CH;AACA,MAAM,4BAA4B,GAAG,UAAU;AAExC,MAAM,uBAAuB,GAAG,IAAI,cAAc,CACvD,yBAAyB,EACzB,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,4BAA4B,EAAE,CAAC,EAAE;AAI1D,MAAO,iBAAkB,SAAQ,aAAa,CAAA;AAC/B,IAAA,IAAI,GAAG,MAAM,CAAC,UAAU,CAAC;AACzB,IAAA,MAAM,GAAG,MAAM,CAAC,uBAAuB,CAAC;AAE3D;;;;;AAKG;IACe,qBAAqB,GACrC,IAAI,CAAC,MAAM,CAAC,WAAW,IAAI,EAAE;;AAI/B;;;;;;;AAOG;AACO,IAAA,GAAG,CAAC,IAAY,EAAA;QACxB,OAAO,CAAA,EAAG,IAAI,CAAC,MAAM,CAAC,OAAO,IAAI,EAAE,CAAA,EAAG,IAAI,CAAA,CAAE;IAC9C;;AAGU,IAAA,OAAO,CAAC,KAAa,EAAA;AAC7B,QAAA,OAAO,kBAAkB,CAAC,KAAK,CAAC;IAClC;AAEA;;;;;;;;;;;;AAYG;AACO,IAAA,UAAU,CAAC,KAAgC,EAAE,SAAiB,EAAE,IAAY,EAAA;AACpF,QAAA,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,IAAI,EAAE,CAAC,MAAM,KAAK,CAAC,EAAE;AAC1D,YAAA,MAAM,IAAI,WAAW,CACnB,SAAS,EACT,CAAA,EAAG,SAAS,CAAA,EAAA,EAAK,IAAI,CAAA,mCAAA,EAAsC,MAAM,CAAC,KAAK,CAAC,CAAA,GAAA,CAAK;gBAC3E,iFAAiF;AACjF,gBAAA,kFAAkF,CACrF;QACH;AACA,QAAA,OAAO,KAAK;IACd;AAEA;;;;;;;;;;;AAWG;IACO,OAAO,CAAI,SAAwC,EAAE,IAAyB,EAAA;QACtF,OAAO,KAAK,CAAC,MAAK;AAChB,YAAA,IAAI,IAAI,CAAC,aAAa,CAAC,SAAS,CAAC,EAAE;AACjC,gBAAA,OAAO,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC;YAChC;YACA,OAAO,IAAI,EAAE,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,KAAc,KAAK,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,SAAS,CAAC,CAAC,CAAC;AACjF,QAAA,CAAC,CAAC;IACJ;;AAGU,IAAA,aAAa,CAAC,SAAiB,EAAA;QACvC,OAAQ,IAAI,CAAC,qBAA2C,CAAC,QAAQ,CAAC,SAAS,CAAC;IAC9E;AAEA;;;;AAIG;AACO,IAAA,OAAO,CAAC,SAAiB,EAAA;AACjC,QAAA,OAAO,UAAU,CACf,MACE,IAAI,WAAW,CACb,gBAAgB,EAChB,CAAA,0CAAA,EAA6C,SAAS,CAAA,CAAA,CAAG,CAC1D,CACJ;IACH;AAEA;;;;;;;;;;;;AAYG;IACO,IAAI,CAAC,KAAc,EAAE,SAAiB,EAAA;AAC9C;;;;;AAKG;AACH,QAAA,IAAI,WAAW,CAAC,EAAE,CAAC,KAAK,CAAC,EAAE;AACzB,YAAA,OAAO,UAAU,CAAC,MAAM,KAAK,CAAC;QAChC;AACA,QAAA,MAAM,QAAQ,GAAG,KAAK,YAAY,iBAAiB,GAAG,KAAK,GAAG,SAAS;QACvE,IAAI,QAAQ,GAAwB,SAAS;AAC7C,QAAA,IAAI,QAAQ,EAAE,MAAM,KAAK,GAAG,EAAE;YAC5B,QAAQ,GAAG,gBAAgB;QAC7B;aAAO,IAAI,CAAC,QAAQ,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE;YAC7C,QAAQ,GAAG,WAAW;QACxB;AACA,QAAA,MAAM,OAAO,GACV,QAAQ,EAAE,KAA0C,EAAE,OAAO;AAC9D,YAAA,QAAQ,EAAE,OAAO;YACjB,CAAA,SAAA,EAAY,SAAS,WAAW;AAClC,QAAA,OAAO,UAAU,CAAC,MAAM,IAAI,WAAW,CAAC,QAAQ,EAAE,OAAO,EAAE,KAAK,CAAC,CAAC;IACpE;;;;AAMU,IAAA,OAAO,CAAI,IAAY,EAAA;AAC/B,QAAA,OAAO,IAAI,CAAC,IAAI,CAAC,GAAG,CAAI,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IACzC;IAEU,QAAQ,CAAI,IAAY,EAAE,IAAa,EAAA;AAC/C,QAAA,OAAO,IAAI,CAAC,IAAI,CAAC,IAAI,CAAI,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,IAAI,CAAC;IAChD;IAEU,OAAO,CAAI,IAAY,EAAE,IAAa,EAAA;AAC9C,QAAA,OAAO,IAAI,CAAC,IAAI,CAAC,GAAG,CAAI,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,IAAI,CAAC;IAC/C;AAEU,IAAA,UAAU,CAAI,IAAY,EAAA;AAClC,QAAA,OAAO,IAAI,CAAC,IAAI,CAAC,MAAM,CAAI,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IAC5C;;AAIA;;;;;;AAMG;AACM,IAAA,QAAQ,CAAC,UAAyB,EAAA;;AAEzC,QAAA,OAAO,IAAI,CAAC,OAAO,CAAC,UAAU,EAAE,MAC9B,IAAI,CAAC,QAAQ,CAAoB,WAAW,EAAE,UAAU,CAAC,CAC1D;IACH;IAES,SAAS,GAAA;AAChB,QAAA,OAAO,IAAI,CAAC,OAAO,CAAC,WAAW,EAAE,MAAM,IAAI,CAAC,OAAO,CAAiB,SAAS,CAAC,CAAC;IACjF;AAES,IAAA,QAAQ,CAAC,SAAiB,EAAA;AACjC,QAAA,OAAO,IAAI,CAAC,OAAO,CAAC,UAAU,EAAE,MAC9B,IAAI,CAAC,OAAO,CACV,WAAW,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,UAAU,CAAC,SAAS,EAAE,UAAU,EAAE,uBAAuB,CAAC,CAAC,CAAA,CAAE,CAC3F,CACF;IACH;;;IAKS,cAAc,CACrB,UAAyB,EACzB,eAAqC,EAAA;QAErC,OAAO,IAAI,CAAC,OAAO,CAAC,gBAAgB,EAAE,MACpC,IAAI,CAAC,QAAQ,CAAmB,UAAU,EAAE,EAAE,UAAU,EAAE,eAAe,EAAE,CAAC,CAC7E;IACH;AAES,IAAA,IAAI,CAAC,IAAY,EAAA;QACxB,OAAO,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,MAC1B,IAAI,CAAC,OAAO,CACV,CAAA,aAAA,EAAgB,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,2BAA2B,CAAC,CAAC,CAAA,CAAE,CAC3F,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,OAAO,KAAK,IAAI,CAAC,iBAAiB,CAAC,OAAO,CAAC,CAAC,CAAC,CAC1D;IACH;AAEA;;;;;;;;;;;;;;;;;;AAkBG;AACO,IAAA,iBAAiB,CAAC,OAAgB,EAAA;QAC1C,MAAM,SAAS,GAAG,OAAoD;AACtE,QAAA,MAAM,MAAM,GAAG,OAAO,SAAS,EAAE,IAAI,KAAK,QAAQ,IAAI,SAAS,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC;QACtF,MAAM,UAAU,GAAG,OAAO,SAAS,EAAE,aAAa,KAAK,QAAQ;AAC/D,QAAA,IAAI,CAAC,MAAM,IAAI,CAAC,UAAU,EAAE;AAC1B,YAAA,MAAM,IAAI,WAAW,CACnB,SAAS,EACT,iFAAiF;gBAC/E,oEAAoE;gBACpE,+EAA+E;gBAC/E,wEAAwE,EAC1E,OAAO,CACR;QACH;AACA,QAAA,OAAO,OAAwB;IACjC;AAEA;;;;AAIG;AACM,IAAA,IAAI,CAAC,UAAyB,EAAA;AACrC,QAAA,OAAO,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,MAC1B,IAAI,CAAC,OAAO,CACV,CAAA,aAAA,EAAgB,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,UAAU,CAAC,UAAU,EAAE,IAAI,EAAE,MAAM,EAAE,2BAA2B,CAAC,CAAC,CAAA,CAAE,EACtG,UAAU,CACX,CACF;IACH;AAEA;;;;;;;;;;;;;;;;;;;;;;;;;;AA0BG;IACM,cAAc,GAAA;AACrB,QAAA,OAAO,IAAI,CAAC,OAAO,CAAC,gBAAgB,EAAE,MAAM,IAAI,CAAC,OAAO,CAAiB,cAAc,CAAC,CAAC;IAC3F;AAEA;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA4BG;IACM,iBAAiB,CACxB,UAAyB,EACzB,QAAgB,EAAA;AAEhB,QAAA,OAAO,IAAI,CAAC,OAAO,CAAC,mBAAmB,EAAE,MACvC,IAAI,CAAC,QAAQ,CACX,CAAA,iBAAA,EAAoB,IAAI,CAAC,OAAO,CAC9B,IAAI,CAAC,UAAU,CAAC,QAAQ,EAAE,mBAAmB,EAAE,kBAAkB,CAAC,CACnE,CAAA,CAAE,EACH,UAAU,CACX,CACF;IACH;;;IAKS,eAAe,GAAA;AACtB,QAAA,OAAO,IAAI,CAAC,OAAO,CAAC,iBAAiB,EAAE,MACrC,IAAI,CAAC,OAAO,CAAyB,cAAc,CAAC,CACrD;IACH;AAEA;;;;AAIG;AACM,IAAA,gBAAgB,CAAC,UAAyB,EAAA;AACjD,QAAA,OAAO,IAAI,CAAC,OAAO,CAAC,kBAAkB,EAAE,MACtC,IAAI,CAAC,QAAQ,CAAuB,cAAc,EAAE,UAAU,CAAC,CAChE;IACH;AAES,IAAA,gBAAgB,CAAC,IAAY,EAAA;AACpC,QAAA,OAAO,IAAI,CAAC,OAAO,CAAC,kBAAkB,EAAE,MACtC,IAAI,CAAC,UAAU,CACb,gBAAgB,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,UAAU,CAAC,IAAI,EAAE,kBAAkB,EAAE,2BAA2B,CAAC,CAAC,CAAA,CAAE,CACvG,CACF;IACH;wGAjWW,iBAAiB,EAAA,IAAA,EAAA,IAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,UAAA,EAAA,CAAA;4GAAjB,iBAAiB,EAAA,CAAA;;4FAAjB,iBAAiB,EAAA,UAAA,EAAA,CAAA;kBAD7B;;AAqWD;;;;;;;;;AASG;AACG,SAAU,wBAAwB,CAAC,MAA6B,EAAA;AACpE,IAAA,OAAO,wBAAwB,CAAC;AAC9B,QAAA;AACE,YAAA,OAAO,EAAE,uBAAuB;AAChC,YAAA,QAAQ,EAAE;AACR,gBAAA,OAAO,EAAE,MAAM,EAAE,OAAO,IAAI,4BAA4B;AACxD,gBAAA,WAAW,EAAE,MAAM,EAAE,WAAW,IAAI,EAAE;AACR,aAAA;AACjC,SAAA;AACD,QAAA,EAAE,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,iBAAiB,EAAE;AACxD,KAAA,CAAC;AACJ;;ACtdA;;;;;;;;;;;;;;;;;;;;;;;;;AAyBG;AAiiBH;AACA;AACA;AACA,MAAM,gBAAgB,GAAG;IACvB,aAAa;IACb,SAAS;IACT,OAAO;IACP,YAAY;IACZ,YAAY;IACZ,SAAS;IACT,gBAAgB;IAChB,WAAW;IACX,aAAa;IACb,YAAY;IACZ,kBAAkB;CACiC;AAkBrD;AACA;AACA;AACA,MAAM,yBAAyB,GAAoE,IAAI;AAEvG;;;;;;;;;;;AAWG;AACI,MAAM,oBAAoB,GAAqC;;AC5mBtE;;;;;;;AAOG;AA4CH;;;;;;;AAOG;AACI,MAAM,uBAAuB,GAAG;AAEvC;;;;;;;;;;AAUG;AACI,MAAM,uBAAuB,GAAG;AAEvC;AACM,SAAU,sBAAsB,CAAC,IAAgC,EAAA;IACrE,IAAI,CAAC,IAAI,EAAE;AACT,QAAA,OAAO,EAAE;IACX;AACA,IAAA,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,IAAI,EAAE;IACpC,IAAI,IAAI,GAAG,EAAE;AACb,IAAA,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE;AAC9B,QAAA,IAAI,OAAO,CAAC,UAAU,EAAE;YACtB,IAAI,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC,GAAG,CAAA,CAAA,EAAI,OAAO,CAAC,UAAU,EAAE,GAAG,OAAO,CAAC,UAAU;QACzE;AACA,QAAA,IAAI,OAAO,CAAC,IAAI,EAAE;AAChB,YAAA,IAAI,IAAI,CAAA,CAAA,EAAI,OAAO,CAAC,IAAI,EAAE;QAC5B;AAAO,aAAA,IAAI,OAAO,CAAC,KAAK,KAAK,SAAS,EAAE;AACtC,YAAA,IAAI,IAAI,CAAA,CAAA,EAAI,OAAO,CAAC,KAAK,GAAG;QAC9B;IACF;AACA,IAAA,IAAI,IAAI,CAAC,KAAK,EAAE;QACd,IAAI,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC,GAAG,CAAA,CAAA,EAAI,IAAI,CAAC,KAAK,EAAE,GAAG,IAAI,CAAC,KAAK;IACzD;AACA,IAAA,OAAO,IAAI;AACb;;AC/FA;;;;;;;;;AASG;;ACTH;;;;;;;;;;;;;;;;;;;;;AAqBG;AAiBG,SAAU,uBAAuB,CACrC,GAAkB,EAClB,SAA+B,EAAA;;;IAI/B,IAAI,GAAG,CAAC,qBAAqB,EAAE,QAAQ,CAAC,SAAS,CAAC,EAAE;AAClD,QAAA,OAAO,KAAK;IACd;AACA,IAAA,MAAM,IAAI,GAAG,aAAa,CAAC,SAAiC;AAC5D,IAAA,MAAM,cAAc,GAAI,GAA4B,CAAC,SAAS,CAAC;IAC/D,OAAO,OAAO,cAAc,KAAK,UAAU,IAAI,cAAc,KAAK,IAAI,CAAC,SAAS,CAAC;AACnF;AAsBM,SAAU,mBAAmB,CAAC,GAAkB,EAAA;IACpD,OAAO;AACL,QAAA,cAAc,EAAE,uBAAuB,CAAC,GAAG,EAAE,gBAAgB,CAAC;AAC9D,QAAA,IAAI,EAAE,uBAAuB,CAAC,GAAG,EAAE,MAAM,CAAC;AAC1C,QAAA,IAAI,EAAE,uBAAuB,CAAC,GAAG,EAAE,MAAM,CAAC;AAC1C,QAAA,cAAc,EAAE,uBAAuB,CAAC,GAAG,EAAE,gBAAgB,CAAC;AAC9D,QAAA,iBAAiB,EAAE,uBAAuB,CAAC,GAAG,EAAE,mBAAmB,CAAC;AACpE,QAAA,eAAe,EAAE,uBAAuB,CAAC,GAAG,EAAE,iBAAiB,CAAC;AAChE,QAAA,gBAAgB,EAAE,uBAAuB,CAAC,GAAG,EAAE,kBAAkB,CAAC;AAClE,QAAA,gBAAgB,EAAE,uBAAuB,CAAC,GAAG,EAAE,kBAAkB,CAAC;KACnE;AACH;AAYA;;;;;;;;;;;;;;AAcG;AACG,SAAU,4BAA4B,CAC1C,YAA6B,EAAA;IAE7B,MAAM,OAAO,GAAa,EAAE;AAC5B,IAAA,IAAI,CAAC,YAAY,CAAC,gBAAgB,EAAE;AAClC,QAAA,OAAO,CAAC,IAAI,CAAC,kBAAkB,CAAC;IAClC;AACA,IAAA,IAAI,CAAC,YAAY,CAAC,cAAc,EAAE;AAChC,QAAA,OAAO,CAAC,IAAI,CAAC,gBAAgB,CAAC;IAChC;AACA,IAAA,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE;AACxB,QAAA,OAAO,EAAE,WAAW,EAAE,IAAI,EAAE;IAC9B;IACA,OAAO;AACL,QAAA,WAAW,EAAE,KAAK;AAClB,QAAA,MAAM,EACJ,OAAO,CAAC,MAAM,KAAK;AACjB,cAAE,iGAAiG;gBACjG;AACF,cAAE,OAAO,CAAC,CAAC,CAAC,KAAK;AACf,kBAAE;AACF,kBAAE,uFAAuF;oBACvF,iGAAiG;KAC1G;AACH;;ACtIA;;;;;;;;;;;;;;AAcG;AAqBH;AACA;AACA;AAEA;;;AAGG;AACH,MAAM,GAAG,GAAG,IAAI;AAEV,SAAU,SAAS,CAAC,QAAgB,EAAA;AACxC,IAAA,OAAO,CAAA,IAAA,EAAO,GAAG,CAAA,EAAG,QAAQ,EAAE;AAChC;AAEA;;;;AAIG;SACa,cAAc,CAAC,QAAgB,EAAE,IAAY,EAAE,KAAc,EAAA;AAC3E,IAAA,MAAM,MAAM,GAAG,KAAK,KAAK,SAAS,GAAG,EAAE,GAAG,CAAA,EAAG,GAAG,CAAA,EAAG,KAAK,EAAE;IAC1D,OAAO,CAAA,EAAA,EAAK,GAAG,CAAA,EAAG,QAAQ,CAAA,EAAG,GAAG,CAAA,EAAG,IAAI,CAAA,EAAG,MAAM,CAAA,CAAE;AACpD;AAEA;AACM,SAAU,eAAe,CAAC,QAAgB,EAAA;AAC9C,IAAA,OAAO,CAAA,GAAA,EAAM,GAAG,CAAA,EAAG,QAAQ,EAAE;AAC/B;AAEA;;;;AAIG;SACa,SAAS,CAAC,cAAsB,EAAE,IAAY,EAAE,KAAc,EAAA;AAC5E,IAAA,MAAM,MAAM,GAAG,KAAK,KAAK,SAAS,GAAG,EAAE,GAAG,CAAA,EAAG,GAAG,CAAA,EAAG,KAAK,EAAE;IAC1D,OAAO,CAAA,IAAA,EAAO,GAAG,CAAA,EAAG,cAAc,CAAA,EAAG,GAAG,CAAA,EAAG,IAAI,CAAA,EAAG,MAAM,CAAA,CAAE;AAC5D;AAEA;;;;;AAKG;AACG,SAAU,cAAc,CAAC,EAAU,EAAA;IACvC,OAAO,EAAE,CAAC,UAAU,CAAC,CAAA,IAAA,EAAO,GAAG,CAAA,CAAE,CAAC,GAAG,EAAE,CAAC,KAAK,CAAC,CAAC,GAAG,GAAG,CAAC,MAAM,CAAC,GAAG,SAAS;AAC3E;AAEM,SAAU,oBAAoB,CAAC,EAAU,EAAA;IAC7C,OAAO,EAAE,CAAC,UAAU,CAAC,CAAA,GAAA,EAAM,GAAG,CAAA,CAAE,CAAC,GAAG,EAAE,CAAC,KAAK,CAAC,CAAC,GAAG,GAAG,CAAC,MAAM,CAAC,GAAG,SAAS;AAC1E;AAEM,SAAU,mBAAmB,CACjC,EAAU,EAAA;IAEV,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,KAAK,GAAG,CAAA,CAAE,CAAC,EAAE;AAC9B,QAAA,OAAO,SAAS;IAClB;AACA,IAAA,MAAM,KAAK,GAAG,EAAE,CAAC,KAAK,CAAC,CAAC,GAAG,GAAG,CAAC,MAAM,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC;AACjD,IAAA,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE;AACpB,QAAA,OAAO,SAAS;IAClB;IACA,MAAM,CAAC,QAAQ,EAAE,IAAI,EAAE,QAAQ,CAAC,GAAG,KAAK;AACxC,IAAA,MAAM,KAAK,GAAG,QAAQ,KAAK,SAAS,GAAG,SAAS,GAAG,MAAM,CAAC,QAAQ,CAAC;IACnE,OAAO;QACL,QAAQ;QACR,IAAI;AACJ,QAAA,KAAK,EAAE,KAAK,KAAK,SAAS,IAAI,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,SAAS,GAAG,KAAK;KACtE;AACH;AA4BA,SAAS,MAAM,CACb,IAAY,EACZ,GAAS,EACT,MAAM,GAAG,IAAI,EAAA;IAEb,OAAO;QACL,IAAI;QACJ,MAAM;QACN,IAAI,EAAE,CAAC,IAAI,KAAK,CAAC,IAAI,CAAC,GAAG,CAAkC,CAAC;;;QAG5D,KAAK,EAAE,CAAC,IAAI,EAAE,KAAK,MAAM,EAAE,GAAG,IAAI,EAAE,CAAC,GAAG,GAAG,KAAK,CAAC,CAAC,CAAC,EAAE,CAAqB;KAC3E;AACH;AAEA,SAAS,IAAI,CACX,IAAY,EACZ,GAAS,EACT,MAAM,GAAG,IAAI,EAAA;IAEb,OAAO;QACL,IAAI;QACJ,MAAM;AACN,QAAA,IAAI,EAAE,CAAC,IAAI,KAAK,CAAE,IAAI,CAAC,GAAG,CAAqC,IAAI,EAAE,EAAE,KAAK,EAAE;;;AAG9E,QAAA,KAAK,EAAE,CAAC,IAAI,EAAE,KAAK,MAChB,EAAE,GAAG,IAAI,EAAE,CAAC,GAAG,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,KAAK,IAAI,IAAI,EAAE,CAAC,EAAE,CAAqB;KAC5E;AACH;AAEA,MAAM,uBAAuB,GAA4B;AACvD,IAAA,IAAI,EAAE,cAAc;AACpB,IAAA,MAAM,EAAE,KAAK;IACb,IAAI,EAAE,CAAC,IAAI,KAAK,CAAC,IAAI,CAAC,MAAM,IAAI,EAAE,EAAE,GAAG,CAAC,CAAC,KAAK,KAAK,KAAK,CAAC,UAAU,CAAC;AACpE,IAAA,KAAK,EAAE,CAAC,IAAI,EAAE,KAAK,KACjB,IAAI,CAAC,MAAM,KAAK;AACd,UAAE;AACF,UAAE;AACE,YAAA,GAAG,IAAI;AACP,YAAA,MAAM,EAAE,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,KAAK,MAAM,EAAE,GAAG,KAAK,EAAE,UAAU,EAAE,KAAK,CAAC,KAAK,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;AAC1F,SAAA;CACR;AAED;;;;;;AAMG;AACH,MAAM,yBAAyB,GAAuC;AACpE,IAAA,IAAI,EAAE,eAAe;AACrB,IAAA,MAAM,EAAE,KAAK;IACb,IAAI,EAAE,CAAC,IAAI,KACT,CAAC,IAAI,CAAC,aAAa,IAAI,EAAE,EAAE,OAAO,CAAC,CAAC,YAAY,KAAK;AACnD,QAAA,YAAY,CAAC,mBAAmB;AAChC,QAAA,YAAY,CAAC,kBAAkB;KAChC,CAAC;AACJ,IAAA,KAAK,EAAE,CAAC,IAAI,EAAE,KAAK,KACjB,IAAI,CAAC,aAAa,KAAK;AACrB,UAAE;AACF,UAAE;AACE,YAAA,GAAG,IAAI;AACP,YAAA,aAAa,EAAE,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC,YAAY,EAAE,KAAK,MAAM;AAC9D,gBAAA,GAAG,YAAY;AACf,gBAAA,mBAAmB,EAAE,KAAK,CAAC,KAAK,GAAG,CAAC,CAAC;gBACrC,kBAAkB,EAAE,KAAK,CAAC,KAAK,GAAG,CAAC,GAAG,CAAC,CAAC;AACzC,aAAA,CAAC,CAAC;AACJ,SAAA;CACR;AAED;;;;;;;;;AASG;AACI,MAAM,gBAAgB,GAEzB;;AAEF,IAAA,WAAW,EAAE,EAA8C;IAC3D,OAAO,EAAE,CAAC,MAAM,CAA2B,QAAQ,EAAE,YAAY,CAAC,CAAC;AACnE,IAAA,KAAK,EAAE;AACL,QAAA,MAAM,CAAgC,SAAS,EAAE,mBAAmB,CAAC;AACrE,QAAA,MAAM,CAAkC,WAAW,EAAE,qBAAqB,CAAC;QAC3E,uBAAuB;AACxB,KAAA;IACD,UAAU,EAAE,CAAC,MAAM,CAA8B,QAAQ,EAAE,YAAY,CAAC,CAAC;IACzE,UAAU,EAAE,CAAC,MAAM,CAA8B,QAAQ,EAAE,YAAY,CAAC,CAAC;IACzE,OAAO,EAAE,CAAC,IAAI,CAAwB,SAAS,EAAE,SAAS,CAAC,CAAC;IAC5D,cAAc,EAAE,CAAC,MAAM,CAAkC,QAAQ,EAAE,YAAY,CAAC,CAAC;IACjF,SAAS,EAAE,CAAC,MAAM,CAA6B,QAAQ,EAAE,YAAY,CAAC,CAAC;IACvE,WAAW,EAAE,CAAC,IAAI,CAAwB,SAAS,EAAE,SAAS,CAAC,CAAC;IAChE,UAAU,EAAE,CAAC,MAAM,CAA8B,QAAQ,EAAE,YAAY,CAAC,CAAC;IACzE,gBAAgB,EAAE,CAAC,yBAAyB,CAAC;;AAc/C;AACM,SAAU,WAAW,CAAC,UAAyB,EAAA;IACnD,MAAM,MAAM,GAAqB,EAAE;AACnC,IAAA,KAAK,MAAM,UAAU,IAAI,oBAAoB,EAAE;QAC7C,MAAM,KAAK,IAAI,UAAU,CAAC,UAAU,CAAC,IAAI,EAAE,CAAiB;QAC5D,KAAK,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,KAAK,KAAI;YAC5B,MAAM,CAAC,IAAI,CAAC,EAAE,UAAU,EAAE,KAAK,EAAE,IAAI,EAAoB,CAAC;AAC5D,QAAA,CAAC,CAAC;IACJ;AACA,IAAA,OAAO,MAAM;AACf;AAEM,SAAU,WAAW,CAAC,UAAyB,EAAE,QAAgB,EAAA;IACrE,OAAO,WAAW,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,CAAC,OAAO,KAAK,OAAO,CAAC,IAAI,CAAC,IAAI,KAAK,QAAQ,CAAC;AAClF;AAcA;AACM,SAAU,uBAAuB,CAAC,OAAuB,EAAA;IAC7D,MAAM,KAAK,GAAG,gBAAgB,CAAC,OAAO,CAAC,UAAU,CAAyC;IAC1F,MAAM,UAAU,GAAyB,EAAE;AAC3C,IAAA,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE;QACxB,MAAM,KAAK,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC;AACrC,QAAA,MAAM,YAAY,GAAG,KAAK,CAAC,MAAM,GAAG,CAAC,IAAI,gBAAgB,CAAC,IAAI,CAAC;QAC/D,KAAK,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,KAAK,KAAI;YAC5B,UAAU,CAAC,IAAI,CAAC;AACd,gBAAA,cAAc,EAAE,OAAO,CAAC,IAAI,CAAC,IAAI;gBACjC,UAAU,EAAE,OAAO,CAAC,UAAU;gBAC9B,IAAI,EAAE,IAAI,CAAC,IAAI;gBACf,KAAK,EAAE,YAAY,GAAG,KAAK,GAAG,SAAS;gBACvC,MAAM,EAAE,IAAI,CAAC,MAAM;gBACnB,UAAU,EAAE,IAAI,KAAK,EAAE,GAAG,SAAS,GAAG,IAAI;AAC3C,aAAA,CAAC;AACJ,QAAA,CAAC,CAAC;IACJ;AACA,IAAA,OAAO,UAAU;AACnB;AAEA;;;;;AAKG;AACH,SAAS,gBAAgB,CAAC,IAA+B,EAAA;AACvD,IAAA,OAAO,IAAI,CAAC,IAAI,KAAK,SAAS,IAAI,IAAI,CAAC,IAAI,KAAK,cAAc,IAAI,IAAI,CAAC,IAAI,KAAK,eAAe;AACjG;AAoCA;;;AAGG;AACG,SAAU,aAAa,CAAC,UAAyB,EAAA;AACrD,IAAA,MAAM,OAAO,GAAG,WAAW,CAAC,UAAU,CAAC;IACvC,MAAM,KAAK,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,KAAK,KAAK,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAE9D,MAAM,KAAK,GAAmB,OAAO,CAAC,GAAG,CAAC,CAAC,KAAK,MAAM;QACpD,EAAE,EAAE,SAAS,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC;AAC9B,QAAA,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC,IAAI;QACrB,UAAU,EAAE,KAAK,CAAC,UAAU;QAC5B,YAAY,EAAE,eAAe,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC;AAC/C,KAAA,CAAC,CAAC;IAEH,MAAM,KAAK,GAAmB,EAAE;AAChC,IAAA,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE;QAC3B,KAAK,MAAM,SAAS,IAAI,uBAAuB,CAAC,KAAK,CAAC,EAAE;YACtD,IAAI,CAAC,SAAS,CAAC,MAAM,IAAI,SAAS,CAAC,UAAU,KAAK,SAAS,EAAE;gBAC3D;YACF;YACA,KAAK,CAAC,IAAI,CAAC;AACT,gBAAA,EAAE,EAAE,SAAS,CAAC,SAAS,CAAC,cAAc,EAAE,SAAS,CAAC,IAAI,EAAE,SAAS,CAAC,KAAK,CAAC;gBACxE,YAAY,EAAE,SAAS,CAAC,UAAU;gBAClC,UAAU,EAAE,SAAS,CAAC,cAAc;gBACpC,IAAI,EAAE,SAAS,CAAC,IAAI;gBACpB,KAAK,EAAE,SAAS,CAAC,KAAK;AACtB,gBAAA,UAAU,EAAE,eAAe,CAAC,SAAS,CAAC,UAAU,CAAC;AACjD,gBAAA,QAAQ,EAAE,cAAc,CAAC,SAAS,CAAC,cAAc,EAAE,SAAS,CAAC,IAAI,EAAE,SAAS,CAAC,KAAK,CAAC;gBACnF,UAAU,EAAE,KAAK,CAAC,GAAG,CAAC,SAAS,CAAC,UAAU,CAAC;AAC5C,aAAA,CAAC;QACJ;IACF;AAEA,IAAA,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE;AACzB;AAWM,SAAU,aAAa,CAAC,OAAuB,EAAA;IACnD,OAAO,uBAAuB,CAAC,OAAO;SACnC,MAAM,CAAC,CAAC,SAAS,KAAK,SAAS,CAAC,MAAM;AACtC,SAAA,GAAG,CAAC,CAAC,SAAS,MAAM;AACnB,QAAA,EAAE,EAAE,cAAc,CAAC,SAAS,CAAC,cAAc,EAAE,SAAS,CAAC,IAAI,EAAE,SAAS,CAAC,KAAK,CAAC;QAC7E,IAAI,EAAE,SAAS,CAAC,IAAI;QACpB,KAAK,EAAE,SAAS,CAAC,KAAK;QACtB,UAAU,EAAE,SAAS,CAAC,UAAU;AACjC,KAAA,CAAC,CAAC;AACP;AAEA;AACA;AACA;AAEA;;;AAGG;AACG,SAAU,uBAAuB,CACrC,UAAa,EACb,IAA6B,EAC7B,QAAgB,EAChB,KAAyB,EACzB,UAA8B,EAAA;AAE9B,IAAA,MAAM,KAAK,GAAG,gBAAgB,CAAC,UAAU,CAAsD;AAC/F,IAAA,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,SAAS,KAAK,SAAS,CAAC,IAAI,KAAK,QAAQ,CAAC;IACnE,IAAI,CAAC,IAAI,EAAE;AACT,QAAA,OAAO,IAAI;IACb;IACA,MAAM,KAAK,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE;AACrC,IAAA,MAAM,QAAQ,GAAG,KAAK,IAAI,CAAC;;;AAG3B,IAAA,OAAO,KAAK,CAAC,MAAM,IAAI,QAAQ,EAAE;AAC/B,QAAA,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC;IACvB;AACA,IAAA,KAAK,CAAC,QAAQ,CAAC,GAAG,UAAU;IAC5B,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC;AAChC;AAEA;;;;;;;;;;;;;;;;;;AAkBG;SACa,aAAa,CAC3B,UAAyB,EACzB,OAAe,EACf,OAAe,EAAA;AAEf,IAAA,IAAI,OAAO,KAAK,OAAO,EAAE;AACvB,QAAA,OAAO,UAAU;IACnB;AAEA,IAAA,MAAM,IAAI,GAAkB,EAAE,GAAG,UAAU,EAAE;AAE7C,IAAA,KAAK,MAAM,UAAU,IAAI,oBAAoB,EAAE;AAC7C,QAAA,MAAM,KAAK,GAAG,UAAU,CAAC,UAAU,CAA6B;QAChE,IAAI,CAAC,KAAK,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE;YAChC;QACF;AACA,QAAA,MAAM,KAAK,GAAG,gBAAgB,CAAC,UAAU,CAAyC;QAClF,MAAM,SAAS,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,KAAI;YACnC,IAAI,MAAM,GAAG,IAAI;AACjB,YAAA,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE;gBACxB,MAAM,KAAK,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC;AAC/B,gBAAA,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,KAAK,IAAI,KAAK,OAAO,CAAC,EAAE;oBAC3C;gBACF;AACA,gBAAA,MAAM,GAAG,IAAI,CAAC,KAAK,CACjB,MAAM,EACN,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,MAAM,IAAI,KAAK,OAAO,GAAG,OAAO,GAAG,IAAI,CAAC,CAAC,CACzD;YACH;AACA,YAAA,IAAI,MAAM,CAAC,IAAI,KAAK,OAAO,EAAE;gBAC3B,MAAM,GAAG,EAAE,GAAG,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE;YACvC;AACA,YAAA,OAAO,MAAM;AACf,QAAA,CAAC,CAAC;;;;AAID,QAAA,IAA2C,CAAC,UAAU,CAAC,GAAG,SAAS;IACtE;AAEA,IAAA,OAAO,IAAI;AACb;AAEA;AACM,SAAU,kBAAkB,CAAC,UAAyB,EAAE,QAAgB,EAAA;IAC5E,IAAI,KAAK,GAAG,CAAC;IACb,KAAK,MAAM,OAAO,IAAI,WAAW,CAAC,UAAU,CAAC,EAAE;QAC7C,KAAK,MAAM,SAAS,IAAI,uBAAuB,CAAC,OAAO,CAAC,EAAE;AACxD,YAAA,IAAI,SAAS,CAAC,UAAU,KAAK,QAAQ,EAAE;gBACrC,KAAK,IAAI,CAAC;YACZ;QACF;IACF;AACA,IAAA,OAAO,KAAK;AACd;;ACrfA;;;;;;;;;;;;;AAaG;AAIH;AACA,MAAM,gBAAgB,GAAyB,IAAI,GAAG,CAAU,oBAAoB,CAAC;AAErF;AACM,SAAU,uBAAuB,CAAC,KAAc,EAAA;AACpD,IAAA,OAAO,gBAAgB,CAAC,GAAG,CAAC,KAAK,CAAC;AACpC;;ACvBA;;;;;;;;;AASG;AAOH;;;;;;;AAOG;AACG,SAAU,qBAAqB,CACnC,UAAsB,EACtB,UAA0B,EAAA;IAE1B,MAAM,KAAK,GAAG,UAAU,CAAC,IAAI,EAAE,QAAQ,GAAG,CAAC,CAAC;AAC5C,IAAA,IAAI,KAAK,EAAE,IAAI,EAAE;QACf,OAAO,KAAK,CAAC,IAAI;IACnB;IACA,MAAM,aAAa,GAAG,UAAU,CAAC,SAAS,GAAG,MAAM,CAAC;IACpD,IAAI,aAAa,EAAE;AACjB,QAAA,OAAO,aAAa;IACtB;IACA,IAAI,CAAC,UAAU,IAAI,KAAK,EAAE,KAAK,KAAK,SAAS,EAAE;AAC7C,QAAA,OAAO,SAAS;IAClB;AACA;;;;;;AAMG;IACH,IAAI,CAAC,uBAAuB,CAAC,KAAK,CAAC,UAAU,CAAC,EAAE;AAC9C,QAAA,OAAO,SAAS;IAClB;AACA,IAAA,OAAO,UAAU,CAAC,KAAK,CAAC,UAAU,CAAC,GAAG,KAAK,CAAC,KAAK,CAAC,EAAE,IAAI;AAC1D;AAEA;AACM,SAAU,kBAAkB,CAAC,UAAsB,EAAA;AACvD,IAAA,OAAO,UAAU,CAAC,IAAI,EAAE,KAAK;AAC/B;AAEA;;;;;;;;;;;AAWG;AACG,SAAU,wBAAwB,CACtC,UAAsB,EACtB,UAAkB,EAClB,KAAa,EACb,KAAa,EAAA;AAEb,IAAA,IAAI,kBAAkB,CAAC,UAAU,CAAC,KAAK,KAAK,EAAE;AAC5C,QAAA,OAAO,KAAK;IACd;AACA,IAAA,OAAO,CAAC,UAAU,CAAC,IAAI,EAAE,QAAQ,IAAI,EAAE,EAAE,IAAI,CAC3C,CAAC,OAAO,KAAK,OAAO,CAAC,UAAU,KAAK,UAAU,IAAI,OAAO,CAAC,KAAK,KAAK,KAAK,CAC1E;AACH;AAEA;;;;AAIG;AACG,SAAU,kBAAkB,CAAC,UAAsB,EAAA;AACvD,IAAA,OAAO,UAAU,CAAC,IAAI,KAAK,uBAAuB;AACpD;AAEA;;;;AAIG;AACG,SAAU,sBAAsB,CAAC,UAAsB,EAAA;IAC3D,OAAO,UAAU,CAAC,IAAI,CAAC,UAAU,CAAC,cAAc,CAAC;AACnD;AAcA,MAAM,aAAa,GAAiD;AAClE,IAAA,IAAI,EAAE,CAAC;AACP,IAAA,OAAO,EAAE,CAAC;AACV,IAAA,KAAK,EAAE,CAAC;CACT;AAEK,SAAU,gBAAgB,CAAC,WAAkC,EAAA;AACjE,IAAA,IAAI,KAAqC;AACzC,IAAA,KAAK,MAAM,UAAU,IAAI,WAAW,EAAE;AACpC,QAAA,IAAI,CAAC,KAAK,IAAI,aAAa,CAAC,UAAU,CAAC,QAAQ,CAAC,GAAG,aAAa,CAAC,KAAK,CAAC,EAAE;AACvE,YAAA,KAAK,GAAG,UAAU,CAAC,QAAQ;QAC7B;IACF;AACA,IAAA,OAAO,KAAK;AACd;AAEA;AACM,SAAU,yBAAyB,CACvC,WAAkC,EAClC,UAA0B,EAAA;AAE1B,IAAA,MAAM,OAAO,GAAG,IAAI,GAAG,EAA8B;AACrD,IAAA,KAAK,MAAM,UAAU,IAAI,WAAW,EAAE;QACpC,MAAM,QAAQ,GAAG,qBAAqB,CAAC,UAAU,EAAE,UAAU,CAAC;AAC9D,QAAA,IAAI,QAAQ,KAAK,SAAS,EAAE;YAC1B;QACF;AACA,QAAA,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,EAAE,WAAW,EAAE,EAAE,EAAE,eAAe,EAAE,KAAK,EAAE;AAClF,QAAA,KAAK,CAAC,WAAW,CAAC,IAAI,CAAC,UAAU,CAAC;QAClC,KAAK,CAAC,eAAe,GAAG,KAAK,CAAC,eAAe,IAAI,kBAAkB,CAAC,UAAU,CAAC;QAC/E,KAAK,CAAC,QAAQ,GAAG,gBAAgB,CAAC,KAAK,CAAC,WAAW,CAAC;AACpD,QAAA,OAAO,CAAC,GAAG,CAAC,QAAQ,EAAE,KAAK,CAAC;IAC9B;AACA,IAAA,OAAO,OAAO;AAChB;AAEA;;;;;AAKG;AACG,SAAU,6BAA6B,CAC3C,WAAkC,EAClC,UAA0B,EAAA;AAE1B,IAAA,OAAO,WAAW,CAAC,MAAM,CACvB,CAAC,UAAU,KAAK,qBAAqB,CAAC,UAAU,EAAE,UAAU,CAAC,KAAK,SAAS,CAC5E;AACH;AAEA;AACM,SAAU,0BAA0B,CACxC,UAAsB,EACtB,UAA0B,EAAA;IAE1B,MAAM,IAAI,GAAG,qBAAqB,CAAC,UAAU,EAAE,UAAU,CAAC;IAC1D,MAAM,QAAQ,GAAG,UAAU,CAAC,IAAI,EAAE,QAAQ,IAAI,EAAE;IAChD,MAAM,IAAI,GAAG;SACV,KAAK,CAAC,CAAC;AACP,SAAA,GAAG,CAAC,CAAC,OAAO,KAAI;AACf,QAAA,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,KAAK,SAAS,GAAG,EAAE,GAAG,CAAA,CAAA,EAAI,OAAO,CAAC,KAAK,GAAG;QACrE,OAAO,CAAA,EAAG,OAAO,CAAC,UAAU,IAAI,EAAE,CAAA,EAAG,OAAO,CAAC,IAAI,GAAG,CAAA,CAAA,EAAI,OAAO,CAAC,IAAI,CAAA,CAAE,GAAG,KAAK,CAAA,CAAE;AAClF,IAAA,CAAC;AACA,SAAA,MAAM,CAAC,CAAC,IAAI,KAAK,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC;AACpC,IAAA,MAAM,KAAK,GAAG,UAAU,CAAC,IAAI,EAAE,KAAK;IACpC,OAAO,CAAC,IAAI,IAAI,aAAa,EAAE,GAAG,IAAI,EAAE,KAAK,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC;AAC5E;AAEA;AACM,SAAU,iBAAiB,CAAC,UAAyB,EAAA;IACzD,OAAO,IAAI,GAAG,CAAC,WAAW,CAAC,UAAU,CAAC,CAAC,GAAG,CAAC,CAAC,OAAO,KAAK,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC7E;;ACzLA;;;;;;;;;;;;;;;AAeG;AAIH;AACO,MAAM,cAAc,GAAG;AAO9B;AACM,SAAU,mBAAmB,CAAC,IAAgB,EAAA;IAClD,MAAM,GAAG,GAAG,IAAI,CAAC,aAAa,GAAG,cAAc,CAAC;IAChD,IAAI,GAAG,KAAK,IAAI,IAAI,OAAO,GAAG,KAAK,QAAQ,EAAE;AAC3C,QAAA,OAAO,SAAS;IAClB;IACA,MAAM,SAAS,GAAG,GAAmC;;;;IAIrD,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE;AAClE,QAAA,OAAO,SAAS;IAClB;AACA,IAAA,OAAO,EAAE,CAAC,EAAE,SAAS,CAAC,CAAW,EAAE,CAAC,EAAE,SAAS,CAAC,CAAW,EAAE;AAC/D;AAEA;AACM,SAAU,oBAAoB,CAClC,IAAW,EACX,QAAyB,EAAA;AAEzB,IAAA,MAAM,aAAa,GAAqB;AACtC,QAAA,IAAI,IAAI,CAAC,aAAa,IAAI,EAAE,CAAC;QAC7B,CAAC,cAAc,GAAG,EAAE,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE;KAC3E;AACD,IAAA,OAAO,EAAE,GAAG,IAAI,EAAE,aAAa,EAAE;AACnC;AAEA;AACM,SAAU,oBAAoB,CAAC,KAA4B,EAAA;IAC/D,OAAO,KAAK,CAAC,MAAM,GAAG,CAAC,IAAI,KAAK,CAAC,KAAK,CAAC,CAAC,IAAI,KAAK,mBAAmB,CAAC,IAAI,CAAC,KAAK,SAAS,CAAC;AAC3F;;AC1DA;;;;;;;;;AASG;AAqCH;;;;;;;;;AASG;AACH,MAAM,cAAc,GAAoE;AACtF,IAAA,WAAW,EAAE;AACX,QAAA,KAAK,EAAE,UAAU;AACjB,QAAA,WAAW,EAAE,4EAA4E;AACzF,QAAA,UAAU,EAAE,CAAC;AACb,QAAA,UAAU,EAAE,CAAC;AACb,QAAA,QAAQ,EAAE,IAAI;AACd,QAAA,MAAM,EAAE,KAAK;AACb,QAAA,mBAAmB,EAAE,KAAK;AAC3B,KAAA;AACD,IAAA,OAAO,EAAE;AACP,QAAA,KAAK,EAAE,QAAQ;AACf,QAAA,WAAW,EAAE,uFAAuF;AACpG,QAAA,UAAU,EAAE,CAAC;AACb,QAAA,UAAU,EAAE,CAAC;AACb,QAAA,QAAQ,EAAE,KAAK;AACf,QAAA,MAAM,EAAE,KAAK;AACb,QAAA,mBAAmB,EAAE,KAAK;AAC3B,KAAA;AACD,IAAA,KAAK,EAAE;AACL,QAAA,KAAK,EAAE,MAAM;AACb,QAAA,WAAW,EAAE,6EAA6E;AAC1F,QAAA,UAAU,EAAE,CAAC;AACb,QAAA,UAAU,EAAE,CAAC;AACb,QAAA,QAAQ,EAAE,KAAK;AACf,QAAA,MAAM,EAAE,KAAK;AACb,QAAA,mBAAmB,EAAE,KAAK;AAC3B,KAAA;AACD,IAAA,UAAU,EAAE;AACV,QAAA,KAAK,EAAE,cAAc;AACrB,QAAA,WAAW,EAAE,sFAAsF;AACnG,QAAA,UAAU,EAAE,CAAC;AACb,QAAA,UAAU,EAAE,CAAC;AACb,QAAA,QAAQ,EAAE,KAAK;AACf,QAAA,MAAM,EAAE,KAAK;AACb,QAAA,mBAAmB,EAAE,KAAK;AAC3B,KAAA;AACD,IAAA,UAAU,EAAE;AACV,QAAA,KAAK,EAAE,gBAAgB;AACvB,QAAA,WAAW,EAAE,0FAA0F;AACvG,QAAA,UAAU,EAAE,CAAC;AACb,QAAA,UAAU,EAAE,CAAC;AACb,QAAA,QAAQ,EAAE,KAAK;AACf,QAAA,MAAM,EAAE,KAAK;AACb,QAAA,mBAAmB,EAAE,KAAK;AAC3B,KAAA;AACD,IAAA,OAAO,EAAE;AACP,QAAA,KAAK,EAAE,QAAQ;AACf,QAAA,WAAW,EAAE,8EAA8E;AAC3F,QAAA,UAAU,EAAE,CAAC;AACb,QAAA,QAAQ,EAAE,KAAK;AACf,QAAA,MAAM,EAAE,KAAK;AACb,QAAA,mBAAmB,EAAE,KAAK;AAC3B,KAAA;AACD,IAAA,cAAc,EAAE;AACd,QAAA,KAAK,EAAE,qBAAqB;AAC5B,QAAA,WAAW,EAAE,6FAA6F;AAC1G,QAAA,UAAU,EAAE,CAAC;AACb,QAAA,UAAU,EAAE,CAAC;AACb,QAAA,QAAQ,EAAE,KAAK;AACf,QAAA,MAAM,EAAE,KAAK;AACb,QAAA,mBAAmB,EAAE,KAAK;AAC3B,KAAA;AACD,IAAA,SAAS,EAAE;AACT,QAAA,KAAK,EAAE,YAAY;AACnB,QAAA,WAAW,EAAE,kEAAkE;AAC/E,QAAA,UAAU,EAAE,CAAC;AACb,QAAA,UAAU,EAAE,CAAC;AACb,QAAA,QAAQ,EAAE,KAAK;AACf,QAAA,MAAM,EAAE,KAAK;AACb,QAAA,mBAAmB,EAAE,IAAI;AAC1B,KAAA;AACD,IAAA,WAAW,EAAE;AACX,QAAA,KAAK,EAAE,qBAAqB;AAC5B,QAAA,WAAW,EAAE,8FAA8F;AAC3G,QAAA,UAAU,EAAE,CAAC;AACb,QAAA,QAAQ,EAAE,KAAK;AACf,QAAA,MAAM,EAAE,KAAK;AACb,QAAA,mBAAmB,EAAE,IAAI;AAC1B,KAAA;AACD,IAAA,UAAU,EAAE;AACV,QAAA,KAAK,EAAE,WAAW;AAClB,QAAA,WAAW,EAAE,uEAAuE;AACpF,QAAA,UAAU,EAAE,CAAC;AACb,QAAA,UAAU,EAAE,CAAC;AACb,QAAA,QAAQ,EAAE,KAAK;AACf,QAAA,MAAM,EAAE,IAAI;AACZ,QAAA,mBAAmB,EAAE,KAAK;AAC3B,KAAA;AACD,IAAA,gBAAgB,EAAE;AAChB,QAAA,KAAK,EAAE,gBAAgB;AACvB,QAAA,WAAW,EAAE,mFAAmF;AAChG,QAAA,UAAU,EAAE,CAAC;AACb,QAAA,QAAQ,EAAE,KAAK;AACf,QAAA,MAAM,EAAE,IAAI;AACZ,QAAA,mBAAmB,EAAE,KAAK;AAC3B,KAAA;CACF;AAED;;;;;AAKG;AACI,MAAM,mBAAmB,GAA+B,oBAAoB,CAAC,GAAG,CACrF,CAAC,UAAU,MAAM,EAAE,UAAU,EAAE,GAAG,cAAc,CAAC,UAAU,CAAC,EAAE,CAAC;AAGjE;AACA,MAAM,aAAa,GAAG,IAAI,GAAG,CAC3B,mBAAmB,CAAC,GAAG,CAAC,CAAC,QAAQ,KAAK,CAAC,QAAQ,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC,CACvE;AAEK,SAAU,eAAe,CAAC,UAAiC,EAAA;IAC/D,MAAM,QAAQ,GAAG,aAAa,CAAC,GAAG,CAAC,UAAU,CAAC;IAC9C,IAAI,CAAC,QAAQ,EAAE;AACb;;;;;;;;;;;;AAYG;QACH,MAAM,IAAI,KAAK,CAAC,CAAA,kBAAA,EAAqB,MAAM,CAAC,UAAU,CAAC,CAAA,gCAAA,CAAkC,CAAC;IAC5F;AACA,IAAA,OAAO,QAAQ;AACjB;AAEA;AACO,MAAM,kBAAkB,GAAqC;AAClE,IAAA,MAAM,EAAE,QAAQ;AAChB,IAAA,SAAS,EAAE,YAAY;AACvB,IAAA,UAAU,EAAE,YAAY;AACxB,IAAA,MAAM,EAAE,QAAQ;;;ACpMlB;;;;;;;;;;;;AAYG;AAWH;AACA,MAAM,SAAS,GAAoD;AACjE,IAAA,WAAW,EAAE,UAAU;AACvB,IAAA,OAAO,EAAE,QAAQ;AACjB,IAAA,KAAK,EAAE,MAAM;AACb,IAAA,UAAU,EAAE,cAAc;AAC1B,IAAA,UAAU,EAAE,gBAAgB;AAC5B,IAAA,OAAO,EAAE,QAAQ;AACjB,IAAA,cAAc,EAAE,WAAW;AAC3B,IAAA,SAAS,EAAE,YAAY;AACvB,IAAA,WAAW,EAAE,YAAY;AACzB,IAAA,UAAU,EAAE,WAAW;AACvB,IAAA,gBAAgB,EAAE,eAAe;CAClC;AAED;AACM,SAAU,kBAAkB,CAChC,UAAyB,EACzB,UAAiC,EAAA;IAEjC,MAAM,IAAI,GAAG,IAAI,GAAG,CAAC,WAAW,CAAC,UAAU,CAAC,CAAC,GAAG,CAAC,CAAC,OAAO,KAAK,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,CAAC;AAC/F,IAAA,MAAM,IAAI,GAAG,SAAS,CAAC,UAAU,CAAC;IAClC,IAAI,KAAK,GAAG,CAAC;AACb,IAAA,OAAO,IAAI,CAAC,GAAG,CAAC,GAAG,IAAI,CAAA,EAAG,KAAK,CAAA,CAAE,CAAC,WAAW,EAAE,CAAC,EAAE;QAChD,KAAK,IAAI,CAAC;IACZ;AACA,IAAA,OAAO,CAAA,EAAG,IAAI,CAAA,EAAG,KAAK,EAAE;AAC1B;AAEA;;;;AAIG;AACG,SAAU,aAAa,CAC3B,UAAyB,EACzB,UAAa,EAAA;IAEb,MAAM,IAAI,GAAG,kBAAkB,CAAC,UAAU,EAAE,UAAU,CAAC;AACvD,IAAA,MAAM,KAAK,GAAG,CAAA,EAAG,eAAe,CAAC,UAAU,CAAC,CAAC,KAAK,IAAI,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,EAAE;AAChF,IAAA,MAAM,IAAI,GAAG,EAAE,IAAI,EAAE,KAAK,EAAE;IAE5B,QAAQ,UAAU;AAChB,QAAA,KAAK,aAAa;AAChB,YAAA,OAAO,EAAE,GAAG,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,EAAE,EAAwC;AACrF,QAAA,KAAK,OAAO;AACV,YAAA,OAAO,EAAE,GAAG,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAwC;AAC/F,QAAA,KAAK,YAAY;YACf,OAAO,EAAE,GAAG,IAAI,EAAE,MAAM,EAAE,EAAE,EAAwC;AACtE,QAAA,KAAK,YAAY;AACf,YAAA,OAAO,EAAE,GAAG,IAAI,EAAE,IAAI,EAAE,YAAY,EAAE,gBAAgB,EAAE,EAAE,EAAwC;AACpG,QAAA,KAAK,SAAS;;;AAGZ,YAAA,OAAO,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,EAAwC;AAC7E,QAAA,KAAK,aAAa;AAChB,YAAA,OAAO,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC,EAAE,aAAa,EAAE,EAAE,EAAE,YAAY,EAAE,EAAE,EAAwC;AAC9G,QAAA,KAAK,WAAW;YACd,OAAO,EAAE,GAAG,IAAI,EAAE,iBAAiB,EAAE,EAAE,EAAwC;AACjF,QAAA,KAAK,YAAY;YACf,OAAO;AACL,gBAAA,GAAG,IAAI;AACP,gBAAA,MAAM,EAAE,UAAU;AAClB,gBAAA,SAAS,EAAE,QAAQ;AACnB,gBAAA,QAAQ,EAAE,2BAA2B,CAAC,UAAU,CAAC;AACjD,gBAAA,MAAM,EAAE,EAAE;aAC2B;AACzC,QAAA,KAAK,kBAAkB;YACrB,OAAO;AACL,gBAAA,GAAG,IAAI;AACP,gBAAA,QAAQ,EAAE,2BAA2B,CAAC,UAAU,CAAC;AACjD,gBAAA,aAAa,EAAE,EAAE;aACoB;AACzC,QAAA;AACE,YAAA,OAAO,IAA0C;;AAEvD;AAEA;;;;AAIG;AACG,SAAU,2BAA2B,CAAC,UAAyB,EAAA;AACnE,IAAA,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU;IAC9B,KAAK,MAAM,IAAI,IAAI,CAAC,IAAI,UAAU,CAAC,UAAU,IAAI,EAAE,CAAC,EAAE,IAAI,UAAU,CAAC,gBAAgB,IAAI,EAAE,CAAC,CAAC,EAAE;AAC7F,QAAA,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC;IACzB;IACA,IAAI,QAAQ,GAAG,CAAC;AAChB,IAAA,OAAO,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE;QACzB,QAAQ,IAAI,CAAC;IACf;AACA,IAAA,OAAO,QAAQ;AACjB;AAEA;AACM,SAAU,eAAe,CAAC,UAAiC,EAAE,IAAgB,EAAA;IACjF,MAAM,MAAM,GAAG,IAA0C;IACzD,QAAQ,UAAU;AAChB,QAAA,KAAK,aAAa;YAChB,OAAQ,MAAM,CAAC,WAAW,CAAY,IAAK,MAAM,CAAC,qBAAqB,CAAY,IAAI,IAAI;AAC7F,QAAA,KAAK,OAAO;AACV,YAAA,OAAQ,MAAM,CAAC,MAAM,CAAY,IAAI,IAAI;AAC3C,QAAA,KAAK,YAAY;AACf,YAAA,OAAQ,MAAM,CAAC,MAAM,CAAY,IAAI,IAAI;QAC3C,KAAK,YAAY,EAAE;YACjB,MAAM,MAAM,GAAI,MAAM,CAAC,QAAQ,CAA2B,EAAE,MAAM,IAAI,CAAC;YACvE,OAAO,MAAM,GAAG,GAAG,MAAM,CAAA,CAAA,EAAI,MAAM,KAAK,CAAC,GAAG,UAAU,GAAG,UAAU,EAAE,GAAG,IAAI;QAC9E;AACA,QAAA,KAAK,SAAS;AACZ,YAAA,OAAO,MAAM,CAAC,WAAW,CAAC,KAAK,IAAI,GAAG,UAAU,GAAI,MAAM,CAAC,WAAW,CAAY,IAAI,IAAI;QAC5F,KAAK,YAAY,EAAE;AACjB,YAAA,MAAM,MAAM,GAAG,MAAM,CAAC,QAAQ,CAAuB;AACrD,YAAA,MAAM,KAAK,GAAG,MAAM,CAAC,WAAW,CAAuB;AACvD,YAAA,OAAO,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,IAAI;QAC5D;QACA,KAAK,kBAAkB,EAAE;YACvB,MAAM,aAAa,GAAI,MAAM,CAAC,eAAe,CAA2B,EAAE,MAAM,IAAI,CAAC;AACrF,YAAA,OAAO;AACL,kBAAE,CAAA,EAAG,aAAa,CAAA,CAAA,EAAI,aAAa,KAAK,CAAC,GAAG,WAAW,GAAG,WAAW,CAAA;kBACnE,IAAI;QACV;AACA,QAAA;AACE,YAAA,OAAO,IAAI;;AAEjB;;ACpJA;;;;;;;;;;;;;;;;;AAiBG;AAoCH;AACA,MAAM,aAAa,GAAG,GAAG;AAEzB;;;;;;;;;;;;;AAaG;SACa,kBAAkB,CAChC,IAAY,EACZ,KAAa,EACb,OAAuB,EAAA;IAEvB,OAAO,EAAE,aAAa,EAAE,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,OAAO,EAAE;AACnD;MAGa,gBAAgB,CAAA;AACV,IAAA,WAAW,GAAG,MAAM,CAAgB,kBAAkB,CAAC,EAAE,EAAE,EAAE,EAAE,QAAQ,CAAC,EAAA,IAAA,SAAA,GAAA,CAAA,EAAA,SAAA,EAAA,aAAA,EAAA,CAAA,GAAA,EAAA,CAAA,CAAC;AACzE,IAAA,eAAe,GAAG,MAAM,CAAgC,SAAS,2DAAC;AAClE,IAAA,cAAc,GAAG,MAAM,CAA+B,SAAS,0DAAC;AACjF;;;;;AAKG;AACc,IAAA,kBAAkB,GAAG,MAAM,CAAoB,EAAE,8DAAC;AACnE;;;;;;;;AAQG;AACc,IAAA,iBAAiB,GAAG,MAAM,CAAC,KAAK,6DAAC;AACjC,IAAA,KAAK,GAAG,MAAM,CAAoB,EAAE,iDAAC;AACrC,IAAA,OAAO,GAAG,MAAM,CAAoB,EAAE,mDAAC;AACxD;;;;;;AAMG;AACc,IAAA,cAAc,GAAG,MAAM,CACtC,IAAI,CAAC,SAAS,CAAC,kBAAkB,CAAC,EAAE,EAAE,EAAE,EAAE,QAAQ,CAAC,CAAC,0DACrD;AACD;;;;;;AAMG;AACc,IAAA,YAAY,GAAG,MAAM,CAAC,KAAK,wDAAC;;AAGpC,IAAA,UAAU,GAAG,IAAI,CAAC,WAAW,CAAC,UAAU,EAAE;;AAG1C,IAAA,WAAW,GAAG,IAAI,CAAC,YAAY,CAAC,UAAU,EAAE;;AAG5C,IAAA,KAAK,GAAG,QAAQ,CAAW,MAAM,aAAa,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,iDAAC;AAEnE,IAAA,iBAAiB,GAAG,IAAI,CAAC,kBAAkB,CAAC,UAAU,EAAE;;AAGxD,IAAA,mBAAmB,GAAG,QAAQ,CACrC,MAAM,IAAI,GAAG,CAAC,IAAI,CAAC,kBAAkB,EAAE,CAAC,+DACzC;AAED;;;AAGG;AACM,IAAA,gBAAgB,GAAG,QAAQ,CAAqB,MAAM,IAAI,CAAC,kBAAkB,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,4DAAC;AAEtF,IAAA,YAAY,GAAG,QAAQ,CAAyB,MAAK;AAC5D,QAAA,MAAM,IAAI,GAAG,IAAI,CAAC,gBAAgB,EAAE;QACpC,OAAO,IAAI,KAAK,SAAS,GAAG,SAAS,GAAG,WAAW,CAAC,IAAI,CAAC,WAAW,EAAE,EAAE,IAAI,CAAC,EAAE,IAAI;AACrF,IAAA,CAAC,wDAAC;AAEF;;;AAGG;AACM,IAAA,gBAAgB,GAAG,IAAI,CAAC,iBAAiB,CAAC,UAAU,EAAE;;AAItD,IAAA,cAAc,GAAG,IAAI,CAAC,eAAe,CAAC,UAAU,EAAE;AAClD,IAAA,aAAa,GAAG,IAAI,CAAC,cAAc,CAAC,UAAU,EAAE;AAEhD,IAAA,WAAW,GAAG,QAAQ,CAC7B,MAAM,IAAI,CAAC,eAAe,EAAE,EAAE,WAAW,IAAI,EAAE,uDAChD;AAEQ,IAAA,OAAO,GAAG,QAAQ,CACzB,MAAM,IAAI,CAAC,eAAe,EAAE,EAAE,OAAO,IAAI,EAAE,mDAC5C;AAEQ,IAAA,cAAc,GAAG,QAAQ,CAChC,MAAM,IAAI,CAAC,eAAe,EAAE,EAAE,cAAc,IAAI,EAAE,0DACnD;AAEQ,IAAA,iBAAiB,GAAG,QAAQ,CACnC,MAAM,IAAI,CAAC,eAAe,EAAE,EAAE,iBAAiB,IAAI,EAAE,6DACtD;;AAGQ,IAAA,YAAY,GAAG,QAAQ,CAAyC,MAAK;AAC5E,QAAA,MAAM,GAAG,GAAG,IAAI,GAAG,EAA6B;QAChD,KAAK,MAAM,MAAM,IAAI,IAAI,CAAC,OAAO,EAAE,EAAE;YACnC,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,QAAQ,EAAE,MAAM,CAAC;QAClC;AACA,QAAA,OAAO,GAAG;AACZ,IAAA,CAAC,wDAAC;AAEF;;;;AAIG;AACM,IAAA,iBAAiB,GAAG,QAAQ,CAA0C,MAC7E,yBAAyB,CAAC,IAAI,CAAC,WAAW,EAAE,EAAE,IAAI,CAAC,WAAW,EAAE,CAAC,6DAClE;AAEQ,IAAA,qBAAqB,GAAG,QAAQ,CAAwB,MAC/D,6BAA6B,CAAC,IAAI,CAAC,WAAW,EAAE,EAAE,IAAI,CAAC,WAAW,EAAE,CAAC,iEACtE;;AAGQ,IAAA,SAAS,GAAG,QAAQ,CAAuC,MAAK;AACvE,QAAA,MAAM,GAAG,GAAG,IAAI,GAAG,EAA2B;QAC9C,KAAK,MAAM,OAAO,IAAI,WAAW,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,EAAE;YACrD,MAAM,QAAQ,GAAG,mBAAmB,CAAC,OAAO,CAAC,IAAI,CAAC;YAClD,IAAI,QAAQ,EAAE;gBACZ,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,EAAE,QAAQ,CAAC;YACtC;QACF;AACA,QAAA,OAAO,GAAG;AACZ,IAAA,CAAC,qDAAC;AAEO,IAAA,OAAO,GAAG,QAAQ,CAAC,MAAM,IAAI,CAAC,KAAK,EAAE,CAAC,MAAM,GAAG,CAAC,mDAAC;AACjD,IAAA,OAAO,GAAG,QAAQ,CAAC,MAAM,IAAI,CAAC,OAAO,EAAE,CAAC,MAAM,GAAG,CAAC,mDAAC;AAE5D;;;;;;;;;AASG;IACM,OAAO,GAAG,QAAQ,CAAC,MAAM,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,KAAK,IAAI,CAAC,cAAc,EAAE,EAAA,IAAA,SAAA,GAAA,CAAA,EAAA,SAAA,EAAA,SAAA,EAAA,CAAA,GAAA,EAAA,CAAA,CAAC;;;;AAM/F;;;;AAIG;AACH,IAAA,IAAI,CAAC,UAAyB,EAAA;AAC5B,QAAA,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,UAAU,CAAC;AAChC,QAAA,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,IAAI,CAAC;;AAE3B,QAAA,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,UAAU,CAAC,CAAC;AACnD,QAAA,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC;AAClB,QAAA,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;AACpB,QAAA,IAAI,CAAC,kBAAkB,CAAC,GAAG,CAAC,EAAE,CAAC;;;;AAI/B,QAAA,IAAI,CAAC,iBAAiB,CAAC,GAAG,CAAC,KAAK,CAAC;AACjC,QAAA,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,SAAS,CAAC;AACnC,QAAA,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,SAAS,CAAC;IACpC;;AAGA,IAAA,mBAAmB,CAAC,MAAqC,EAAA;AACvD,QAAA,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,MAAM,CAAC;IAClC;AAEA,IAAA,kBAAkB,CAAC,MAAoC,EAAA;AACrD,QAAA,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,MAAM,CAAC;IACjC;AAEA;;;;;;;;AAQG;AACH,IAAA,SAAS,CAAC,KAAqB,EAAA;AAC7B,QAAA,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,IAAI,IAAI,CAAC,WAAW,EAAE,CAAC,CAAC;IACtE;AAEA;;;;;AAKG;AACH,IAAA,MAAM,CAAC,QAA4B,EAAA;AACjC,QAAA,IAAI,CAAC,kBAAkB,CAAC,GAAG,CAAC,QAAQ,KAAK,SAAS,GAAG,EAAE,GAAG,CAAC,QAAQ,CAAC,CAAC;IACvE;;AAGA,IAAA,UAAU,CAAC,SAA4B,EAAA;AACrC,QAAA,IAAI,CAAC,kBAAkB,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC;IAC7D;AAEA;;;;;;;;;;AAUG;AACH,IAAA,cAAc,CAAC,QAAgB,EAAA;AAC7B,QAAA,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC;AACrB,QAAA,IAAI,CAAC,iBAAiB,CAAC,GAAG,CAAC,IAAI,CAAC;IAClC;IAEA,eAAe,GAAA;AACb,QAAA,IAAI,CAAC,iBAAiB,CAAC,GAAG,CAAC,KAAK,CAAC;IACnC;;;;AAMA;;;;;;;;AAQG;AACH,IAAA,eAAe,CAAC,KAA6D,EAAA;AAC3E,QAAA,IAAI,CAAC,MAAM,CAAC,EAAE,GAAG,IAAI,CAAC,WAAW,EAAE,EAAE,GAAG,KAAK,EAAE,CAAC;IAClD;IAEA,OAAO,CAAkC,UAAa,EAAE,IAA6B,EAAA;AACnF,QAAA,MAAM,UAAU,GAAG,IAAI,CAAC,WAAW,EAAE;QACrC,MAAM,QAAQ,IAAI,UAAU,CAAC,UAAU,CAAC,IAAI,EAAE,CAA8B;AAC5E,QAAA,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,cAAc,CAAC,UAAU,EAAE,UAAU,EAAE,CAAC,GAAG,QAAQ,EAAE,IAAI,CAAC,CAAC,CAAC;IAC/E;AAEA,IAAA,UAAU,CACR,UAAa,EACb,QAAgB,EAChB,KAAuC,EAAA;AAEvC,QAAA,MAAM,UAAU,GAAG,IAAI,CAAC,WAAW,EAAE;QACrC,MAAM,QAAQ,IAAI,UAAU,CAAC,UAAU,CAAC,IAAI,EAAE,CAA8B;QAC5E,IAAI,OAAO,GAAG,KAAK;QACnB,MAAM,IAAI,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC,IAAI,KAAI;AACjC,YAAA,IAAI,IAAI,CAAC,IAAI,KAAK,QAAQ,EAAE;AAC1B,gBAAA,OAAO,IAAI;YACb;YACA,OAAO,GAAG,IAAI;;;;YAId,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,GAAG,IAAI,EAAE,GAAG,KAA6D;AACjG,YAAA,KAAK,QAAQ;AACb,YAAA,OAAO,EAAE,GAAG,IAAI,EAAE,GAAG,IAAI,EAAE;AAC7B,QAAA,CAAC,CAAC;QACF,IAAI,CAAC,OAAO,EAAE;YACZ;QACF;AACA,QAAA,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,cAAc,CAAC,UAAU,EAAE,UAAU,EAAE,IAAI,CAAC,CAAC;IAChE;AAEA;;;;AAIG;AACH,IAAA,UAAU,CAAC,QAAgB,EAAA;AACzB,QAAA,MAAM,UAAU,GAAG,IAAI,CAAC,WAAW,EAAE;QACrC,MAAM,OAAO,GAAG,WAAW,CAAC,UAAU,EAAE,QAAQ,CAAC;QACjD,IAAI,CAAC,OAAO,EAAE;YACZ;QACF;;;AAGA,QAAA,MAAM,WAAW,GAAG,IAAI,CAAC,iBAAiB,EAAE,IAAI,IAAI,CAAC,gBAAgB,EAAE,KAAK,QAAQ;AACpF,QAAA,MAAM,QAAQ,IAAI,UAAU,CAAC,OAAO,CAAC,UAAU,CAAC,IAAI,EAAE,CAAiB;AACvE,QAAA,MAAM,IAAI,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,IAAI,KAAK,IAAI,CAAC,IAAI,KAAK,QAAQ,CAAC;AAC9D,QAAA,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,cAAc,CAAC,UAAU,EAAE,OAAO,CAAC,UAAU,EAAE,IAAI,CAAC,CAAC;;;;AAItE,QAAA,MAAM,QAAQ,GAAG,IAAI,CAAC,kBAAkB,EAAE;AAC1C,QAAA,IAAI,QAAQ,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE;AAC/B,YAAA,IAAI,CAAC,kBAAkB,CAAC,GAAG,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,IAAI,KAAK,IAAI,KAAK,QAAQ,CAAC,CAAC;QAC3E;;;;;QAKA,IAAI,WAAW,EAAE;AACf,YAAA,IAAI,CAAC,iBAAiB,CAAC,GAAG,CAAC,KAAK,CAAC;QACnC;IACF;AAEA;;;AAGG;IACH,UAAU,CAAC,OAAe,EAAE,OAAe,EAAA;QACzC,IAAI,OAAO,KAAK,OAAO,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE;YAC/C;QACF;AACA,QAAA,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,IAAI,CAAC,WAAW,EAAE,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;;AAEhE,QAAA,MAAM,QAAQ,GAAG,IAAI,CAAC,kBAAkB,EAAE;AAC1C,QAAA,IAAI,QAAQ,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE;AAC9B,YAAA,IAAI,CAAC,kBAAkB,CAAC,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,IAAI,MAAM,IAAI,KAAK,OAAO,GAAG,OAAO,GAAG,IAAI,CAAC,CAAC,CAAC;QAC1F;IACF;;AAGA,IAAA,eAAe,CAAC,QAAgB,EAAA;QAC9B,OAAO,kBAAkB,CAAC,IAAI,CAAC,WAAW,EAAE,EAAE,QAAQ,CAAC;IACzD;AAEA;;;AAGG;IACH,YAAY,CAAkC,UAAa,EAAE,QAAyB,EAAA;AACpF,QAAA,MAAM,UAAU,GAAG,IAAI,CAAC,WAAW,EAAE;AACrC,QAAA,MAAM,IAAI,GAAG,oBAAoB,CAAC,aAAa,CAAC,UAAU,EAAE,UAAU,CAAC,EAAE,QAAQ,CAAC;QAClF,MAAM,QAAQ,IAAI,UAAU,CAAC,UAAU,CAAC,IAAI,EAAE,CAA8B;AAC5E,QAAA,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,cAAc,CAAC,UAAU,EAAE,UAAU,EAAE,CAAC,GAAG,QAAQ,EAAE,IAAI,CAAC,CAAC,CAAC;QAC7E,OAAO,IAAI,CAAC,IAAI;IAClB;AAEA;;;;AAIG;AACH,IAAA,QAAQ,CAAC,QAAgB,EAAE,QAAyB,EAAE,OAA8B,EAAA;AAClF,QAAA,MAAM,UAAU,GAAG,IAAI,CAAC,WAAW,EAAE;QACrC,MAAM,OAAO,GAAG,WAAW,CAAC,UAAU,EAAE,QAAQ,CAAC;QACjD,IAAI,CAAC,OAAO,EAAE;YACZ;QACF;AACA,QAAA,MAAM,QAAQ,IAAI,UAAU,CAAC,OAAO,CAAC,UAAU,CAAC,IAAI,EAAE,CAAiB;AACvE,QAAA,MAAM,IAAI,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC,IAAI,KAC7B,IAAI,CAAC,IAAI,KAAK,QAAQ,GAAG,oBAAoB,CAAC,IAAI,EAAE,QAAQ,CAAC,GAAG,IAAI,CACrE;AACD,QAAA,MAAM,OAAO,GAAG,IAAI,CAAC,cAAc,CAAC,UAAU,EAAE,OAAO,CAAC,UAAU,EAAE,IAAI,CAAC;AACzE,QAAA,IAAI,OAAO,EAAE,MAAM,EAAE;AACnB,YAAA,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,OAAO,CAAC;YAC7B;QACF;AACA,QAAA,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC;IACtB;AAEA;;;AAGG;AACH,IAAA,cAAc,CAAC,SAA+C,EAAA;AAC5D,QAAA,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,WAAW,EAAE,EAAE,SAAS,CAAC,CAAC;IAChE;AAEA;;;;;;;;;;;AAWG;AACH,IAAA,qBAAqB,CAAC,SAA+C,EAAA;AACnE,QAAA,MAAM,QAAQ,GAAG,CAAC,IAAI,CAAC,OAAO,EAAE;AAChC,QAAA,MAAM,IAAI,GAAG,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,WAAW,EAAE,EAAE,SAAS,CAAC;AAC9D,QAAA,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,IAAI,CAAC;QAC1B,IAAI,QAAQ,EAAE;AACZ,YAAA,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;QAC/C;IACF;AAEA;;;;AAIG;AACH,IAAA,OAAO,CACL,cAAsB,EACtB,IAAY,EACZ,UAAkB,EAClB,KAAc,EAAA;QAEd,IAAI,CAAC,cAAc,CAAC,cAAc,EAAE,IAAI,EAAE,KAAK,EAAE,UAAU,CAAC;IAC9D;;AAGA,IAAA,UAAU,CAAC,cAAsB,EAAE,IAAY,EAAE,KAAc,EAAA;QAC7D,IAAI,CAAC,cAAc,CAAC,cAAc,EAAE,IAAI,EAAE,KAAK,EAAE,SAAS,CAAC;IAC7D;;;;IAMA,IAAI,GAAA;AACF,QAAA,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,EAAE;AACzB,QAAA,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE;YACrB;QACF;QACA,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC;AACtC,QAAA,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;QACjC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC,OAAO,EAAE,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC;AACzE,QAAA,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAkB,CAAC;IAC7D;IAEA,IAAI,GAAA;AACF,QAAA,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,EAAE;AAC7B,QAAA,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE;YACvB;QACF;QACA,MAAM,QAAQ,GAAG,MAAM,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC;AAC1C,QAAA,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;QACrC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC,KAAK,EAAE,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC;AACrE,QAAA,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAkB,CAAC;IAC7D;;;;AAMQ,IAAA,cAAc,CACpB,cAAsB,EACtB,IAAY,EACZ,KAAyB,EACzB,UAA8B,EAAA;AAE9B,QAAA,MAAM,UAAU,GAAG,IAAI,CAAC,WAAW,EAAE;QACrC,MAAM,OAAO,GAAG,WAAW,CAAC,UAAU,EAAE,cAAc,CAAC;QACvD,IAAI,CAAC,OAAO,EAAE;YACZ;QACF;AACA,QAAA,MAAM,QAAQ,IAAI,UAAU,CAAC,OAAO,CAAC,UAAU,CAAC,IAAI,EAAE,CAAiB;AACvE,QAAA,MAAM,IAAI,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC,IAAI,KAC7B,IAAI,CAAC,IAAI,KAAK;AACZ,cAAE,uBAAuB,CAAC,OAAO,CAAC,UAAU,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,UAAU;cACzE,IAAI,CACT;AACD,QAAA,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,cAAc,CAAC,UAAU,EAAE,OAAO,CAAC,UAAU,EAAE,IAAI,CAAC,CAAC;IACxE;;IAGQ,aAAa,CACnB,UAAyB,EACzB,SAA+C,EAAA;QAE/C,IAAI,IAAI,GAAG,UAAU;AACrB,QAAA,KAAK,MAAM,UAAU,IAAI,oBAAoB,EAAE;AAC7C,YAAA,MAAM,KAAK,GAAG,IAAI,CAAC,UAAU,CAA6B;YAC1D,IAAI,CAAC,KAAK,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE;gBAChC;YACF;YACA,MAAM,OAAO,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,KAAI;gBACjC,MAAM,QAAQ,GAAG,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC;AACzC,gBAAA,OAAO,QAAQ,GAAG,oBAAoB,CAAC,IAAI,EAAE,QAAQ,CAAC,GAAG,IAAI;AAC/D,YAAA,CAAC,CAAC;YACF,IAAI,GAAG,IAAI,CAAC,cAAc,CAAC,IAAI,EAAE,UAAU,EAAE,OAAO,CAAC;QACvD;AACA,QAAA,OAAO,IAAI;IACb;AAEA;;;;AAIG;AACK,IAAA,cAAc,CACpB,UAAyB,EACzB,UAAiC,EACjC,KAAyB,EAAA;AAEzB,QAAA,OAAO,EAAE,GAAG,UAAU,EAAE,CAAC,UAAU,GAAG,KAAK,CAAC,KAAK,EAAE,EAAmB;IACxE;AAEQ,IAAA,MAAM,CAAC,IAAmB,EAAA;QAChC,MAAM,QAAQ,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC;QACnD,MAAM,IAAI,GAAG,CAAC,GAAG,IAAI,CAAC,KAAK,EAAE,EAAE,QAAQ,CAAC;AACxC,QAAA,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,MAAM,GAAG,aAAa,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,GAAG,aAAa,CAAC,GAAG,IAAI,CAAC;;;AAG5F,QAAA,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;AACpB,QAAA,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,IAAI,CAAC;IAC5B;wGAxfW,gBAAgB,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,UAAA,EAAA,CAAA;4GAAhB,gBAAgB,EAAA,CAAA;;4FAAhB,gBAAgB,EAAA,UAAA,EAAA,CAAA;kBAD5B;;;AC9ED;;;;;;;;;;;;;;;;;;;;AAoBG;MAcU,eAAe,CAAA;AACT,IAAA,GAAG,GAAG,MAAM,CAAC,aAAa,CAAC;AAC3B,IAAA,UAAU,GAAG,MAAM,CAAC,UAAU,CAAC;AAEhD;;;;AAIG;AACM,IAAA,YAAY,GAAG,mBAAmB,CAAC,IAAI,CAAC,GAAG,CAAC;;AAG5C,IAAA,aAAa,GAAG,4BAA4B,CAAC,IAAI,CAAC,YAAY,CAAC;AAEvD,IAAA,UAAU,GAAG,MAAM,CAAkC,EAAE,sDAAC;AACxD,IAAA,UAAU,GAAG,MAAM,CAAC,KAAK,sDAAC;AAC1B,IAAA,WAAW,GAAG,MAAM,CAAC,KAAK,uDAAC;AAC3B,IAAA,MAAM,GAAG,MAAM,CAAqB,IAAI,kDAAC;AACzC,IAAA,UAAU,GAAG,MAAM,CAAC,KAAK,sDAAC;AAC1B,IAAA,mBAAmB,GAAG,MAAM,CAA6B,SAAS,+DAAC;;AAG3E,IAAA,SAAS,GAAG,IAAI,CAAC,UAAU,CAAC,UAAU,EAAE;AAEjD;;;;AAIG;AACM,IAAA,SAAS,GAAG,IAAI,CAAC,UAAU,CAAC,UAAU,EAAE;AACxC,IAAA,UAAU,GAAG,IAAI,CAAC,WAAW,CAAC,UAAU,EAAE;AAE1C,IAAA,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC,UAAU,EAAE;;AAGhC,IAAA,SAAS,GAAG,IAAI,CAAC,UAAU,CAAC,UAAU,EAAE;AAEjD;;;;AAIG;AACM,IAAA,kBAAkB,GAAG,IAAI,CAAC,mBAAmB,CAAC,UAAU,EAAE;IAE1D,OAAO,GAAG,QAAQ,CAAC,MAAM,IAAI,CAAC,UAAU,EAAE,IAAI,IAAI,CAAC,UAAU,EAAE,CAAC,MAAM,KAAK,CAAC,mDAAC;AAEtF,IAAA,WAAA,GAAA;;;QAGE,IAAI,CAAC,OAAO,EAAE;;;;AAKd,QAAA,IAAI,IAAI,CAAC,YAAY,CAAC,cAAc,EAAE;AACpC,YAAA,IAAI,CAAC;AACF,iBAAA,cAAc;AACd,iBAAA,IAAI,CACH,GAAG,CAAC,CAAC,WAAW,KAAK,IAAI,CAAC,mBAAmB,CAAC,GAAG,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC,EACvE,UAAU,CAAC,MAAM,KAAK,CAAC,EACvB,kBAAkB,CAAC,IAAI,CAAC,UAAU,CAAC;AAEpC,iBAAA,SAAS,EAAE;QAChB;IACF;AAEA;;;AAGG;IACH,OAAO,GAAA;AACL,QAAA,IAAI,CAAC,IAAI,CAAC,YAAY,CAAC,eAAe,EAAE;YACtC;QACF;AACA,QAAA,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC;AACzB,QAAA,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC;AACrB,QAAA,IAAI,CAAC;AACF,aAAA,eAAe;AACf,aAAA,IAAI,CACH,GAAG,CAAC,CAAC,SAAS,KAAI;AAChB,YAAA,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,SAAS,CAAC;AAC9B,YAAA,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,KAAK,CAAC;AAC1B,YAAA,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC;AAC3B,QAAA,CAAC,CAAC,EACF,UAAU,CAAC,CAAC,KAAc,KAAK,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,MAAM,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,EAClF,kBAAkB,CAAC,IAAI,CAAC,UAAU,CAAC;AAEpC,aAAA,SAAS,EAAE;IAChB;AAEA;;;;;;;;;;;;AAYG;AACH,IAAA,MAAM,CAAC,IAAY,EAAE,KAAa,EAAE,OAAuB,EAAA;QACzD,OAAO,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,gBAAgB,CAAC,kBAAkB,CAAC,IAAI,EAAE,KAAK,EAAE,OAAO,CAAC,CAAC,CAAC;IACzF;AAEA;;;;;;;;;AASG;AACH,IAAA,SAAS,CACP,UAAkB,EAClB,IAAY,EACZ,KAAa,EAAA;AAEb,QAAA,OAAO,IAAI,CAAC,MAAM,CAChB,IAAI,CAAC;aACF,IAAI,CAAC,UAAU;aACf,IAAI,CAAC,SAAS,CAAC,CAAC,MAAM,KAAK,IAAI,CAAC,GAAG,CAAC,gBAAgB,CAAC,EAAE,GAAG,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,CACtF;IACH;;AAGA,IAAA,MAAM,CAAC,IAAY,EAAA;AACjB,QAAA,IAAI,CAAC,IAAI,CAAC,YAAY,CAAC,gBAAgB,EAAE;YACvC;QACF;AACA,QAAA,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,IAAI,CAAC;AAC1B,QAAA,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC;AACrB,QAAA,IAAI,CAAC;aACF,gBAAgB,CAAC,IAAI;AACrB,aAAA,IAAI,CACH,GAAG,CAAC,MAAK;AACP,YAAA,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,KAAK,CAAC;YAC3B,IAAI,CAAC,OAAO,EAAE;AAChB,QAAA,CAAC,CAAC,EACF,UAAU,CAAC,CAAC,KAAc,KAAK,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,MAAM,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,EACnF,kBAAkB,CAAC,IAAI,CAAC,UAAU,CAAC;AAEpC,aAAA,SAAS,EAAE;IAChB;IAEA,YAAY,GAAA;AACV,QAAA,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC;IACvB;AAEA;;;;AAIG;AACK,IAAA,MAAM,CAAC,MAAwC,EAAA;AACrD,QAAA,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC;QACrB,OAAO,MAAM,CAAC,IAAI;AAChB;;;;;AAKG;AACH,QAAA,GAAG,CAAC,CAAC,OAAO,KAAI;AACd,YAAA,IAAI,OAAO,OAAO,EAAE,IAAI,KAAK,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,MAAM,KAAK,CAAC,EAAE;AACzE,gBAAA,MAAM,IAAI,WAAW,CACnB,SAAS,EACT,uFAAuF;oBACrF,oFAAoF;oBACpF,oEAAoE,EACtE,OAAO,CACR;YACH;AACA,YAAA,OAAO,OAAO;QAChB,CAAC,CAAC,EACF,GAAG,CAAC;YACF,SAAS,EAAE,MAAM,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,IAAI,CAAC;YAC3C,IAAI,EAAE,MAAK;AACT,gBAAA,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,KAAK,CAAC;gBAC3B,IAAI,CAAC,OAAO,EAAE;YAChB,CAAC;AACF,SAAA,CAAC,EACF,UAAU,CAAC,CAAC,KAAc,KAAK,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,MAAM,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CACpF;IACH;;IAGQ,IAAI,CAAC,KAAc,EAAE,KAAiB,EAAA;AAC5C,QAAA,KAAK,EAAE;AACP,QAAA,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC;AACvC,QAAA,OAAO,KAAK;IACd;;AAGQ,IAAA,UAAU,CAAC,KAAc,EAAA;AAC/B,QAAA,IAAI,WAAW,CAAC,EAAE,CAAC,KAAK,CAAC,EAAE;AACzB,YAAA,OAAO,KAAK;QACd;AACA,QAAA,MAAM,OAAO,GAAG,KAAK,YAAY,KAAK,GAAG,KAAK,CAAC,OAAO,GAAG,8BAA8B;QACvF,OAAO,IAAI,WAAW,CAAC,WAAW,EAAE,OAAO,EAAE,KAAK,CAAC;IACrD;wGA5MW,eAAe,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,UAAA,EAAA,CAAA;4GAAf,eAAe,EAAA,CAAA;;4FAAf,eAAe,EAAA,UAAA,EAAA,CAAA;kBAD3B;;;ACjCD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA+CG;AA+BH;;;;;;;;;;;;;;;;;AAiBG;AACG,SAAU,WAAW,CAAC,KAAc,EAAA;AACxC,IAAA,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,EAAE,IAAI,KAAK,KAAK,KAAK,EAAE;AAC5E,QAAA,OAAO,KAAK;IACd;AACA,IAAA,OAAO,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,GAAG,KAAK,CAAC,MAAM,GAAG,CAAC,GAAG,IAAI;AACvD;AAEA;;;;;;AAMG;AACG,SAAU,kBAAkB,CAChC,IAAmB,EACnB,UAAuB,EAAA;IAEvB,MAAM,UAAU,GAAoC,EAAE;AACtD,IAAA,KAAK,MAAM,CAAC,KAAK,EAAE,SAAS,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,UAAU,CAAyC,EAAE;AACnG,QAAA,UAAU,CAAC,KAAK,CAAC,GAAG,QAAQ,CAAC,MAAK;AAChC,YAAA,MAAM,OAAO,GAAG,IAAI,EAAE;AACtB,YAAA,OAAO,SAAS,CAAC,OAAO,CAAC,IAAI,WAAW,CAAE,OAAmC,CAAC,KAAK,CAAC,CAAC;AACvF,QAAA,CAAC,CAAC;IACJ;AACA,IAAA,OAAO,UAA6C;AACtD;AAEA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAwCG;SACa,oBAAoB,CAClC,IAAW,EACX,MAAsB,EACtB,UAAuB,EAAA;IAEvB,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC,UAAU,CAAyC;IAClF,MAAM,IAAI,GAAG,EAAE,GAAG,IAAI,EAAE,GAAG,MAAM,EAA6B;AAC9D,IAAA,MAAM,KAAK,GAA4B,EAAE,GAAG,MAAM,EAAE;IACpD,IAAI,gBAAgB,GAAG,IAAI;IAC3B,OAAO,gBAAgB,EAAE;QACvB,gBAAgB,GAAG,KAAK;QACxB,KAAK,MAAM,CAAC,KAAK,EAAE,SAAS,CAAC,IAAI,OAAO,EAAE;;;;AAIxC,YAAA,IAAI,IAAI,CAAC,KAAK,CAAC,KAAK,SAAS,IAAI,SAAS,CAAC,IAAa,CAAC,EAAE;gBACzD;YACF;AACA,YAAA,IAAI,CAAC,KAAK,CAAC,GAAG,SAAS;AACvB,YAAA,KAAK,CAAC,KAAK,CAAC,GAAG,SAAS;YACxB,gBAAgB,GAAG,IAAI;QACzB;IACF;AACA,IAAA,OAAO,KAAuB;AAChC;AAEA;;;;;AAKG;AACI,MAAM,0BAA0B,GAAG;IACxC,SAAS,EAAE,CAAC,IAAI,KAAK,IAAI,CAAC,IAAI,KAAK,OAAO;IAC1C,UAAU,EAAE,CAAC,IAAI,KAAK,IAAI,CAAC,IAAI,KAAK,KAAK;IACzC,mBAAmB,EAAE,CAAC,IAAI,KAAK,IAAI,CAAC,IAAI,KAAK,iBAAiB;;AAGhE;;;;AAIG;AACI,MAAM,qBAAqB,GAAG;IACnC,QAAQ,EAAE,CAAC,IAAI,KAAK,CAAC,IAAI,CAAC,SAAS;IACnC,SAAS,EAAE,CAAC,IAAI,KAAK,CAAC,IAAI,CAAC,SAAS;IACpC,mBAAmB,EAAE,CAAC,IAAI,KAAK,CAAC,CAAC,IAAI,CAAC,SAAS;;AAGjD;;;AAGG;AACI,MAAM,mBAAmB,GAAG;IACjC,0BAA0B,EAAE,CAAC,IAAI,KAAK,IAAI,CAAC,IAAI,KAAK,QAAQ;;AAG9D;AACO,MAAM,6BAA6B,GAAG;IAC3C,eAAe,EAAE,CAAC,IAAI,KAAK,CAAC,CAAC,IAAI,CAAC,qBAAqB;;AAGzD;;;;;AAKG;AACI,MAAM,wBAAwB,GAAG;IACtC,gBAAgB,EAAE,CAAC,IAAI,KAAK,IAAI,CAAC,IAAI,KAAK,OAAO;IACjD,aAAa,EAAE,CAAC,IAAI,KAAK,IAAI,CAAC,IAAI,KAAK,OAAO;IAC9C,WAAW,EAAE,CAAC,IAAI,KAAK,IAAI,CAAC,IAAI,KAAK,iBAAiB;IACtD,OAAO,EAAE,CAAC,IAAI,KAAK,IAAI,CAAC,IAAI,KAAK,iBAAiB;;AAGpD;;;;;;;;;;;;;;;;;;;;AAoBG;AACI,MAAM,wBAAwB,GAAG;IACtC,SAAS,EAAE,CAAC,IAAI,KAAK,IAAI,CAAC,MAAM,KAAK,UAAU;IAC/C,SAAS,EAAE,CAAC,IAAI,KAAK,IAAI,CAAC,MAAM,KAAK,UAAU;IAC/C,mBAAmB,EAAE,CAAC,IAAI,KAAK,IAAI,CAAC,SAAS,KAAK,QAAQ;IAC1D,eAAe,EAAE,CAAC,IAAI,KAAK,IAAI,CAAC,MAAM,KAAK,UAAU;IACrD,wBAAwB,EAAE,CAAC,IAAI,KAAK,IAAI,CAAC,MAAM,KAAK,UAAU;IAC9D,eAAe,EAAE,CAAC,IAAI,KAAK,IAAI,CAAC,MAAM,KAAK,UAAU;IACrD,WAAW,EAAE,CAAC,IAAI,KAAK,IAAI,CAAC,MAAM,KAAK,MAAM;IAC7C,UAAU,EAAE,CAAC,IAAI,KAAK,IAAI,CAAC,MAAM,KAAK,KAAK;;;AC7Q7C;;;;;;;;;;;;;;;;AAgBG;AA2CH;;;;;;;;;;;;;;AAcG;AACG,SAAU,wBAAwB,CACtC,MAA2C,EAAA;IAE3C,IAAI,CAAC,MAAM,EAAE;AACX,QAAA,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE;IAC5B;AACA,IAAA,MAAM,WAAW,GAAG,MAAM,CAAC,WAAW,IAAI,EAAE;AAC5C,IAAA,IAAI,WAAW,CAAC,MAAM,GAAG,CAAC,EAAE;AAC1B;;;;;AAKG;QACH,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,CACxB,CAAC,EACD,WAAW,CAAC,SAAS,CAAC,CAAC,KAAK,KAAK,KAAK,CAAC,IAAI,KAAK,uBAAuB,CAAC,CACzE;QACD,OAAO;AACL,YAAA,IAAI,EAAE,QAAQ;AACd,YAAA,IAAI,EAAE,WAAW,CAAC,SAAS,CAAC;AAC5B,YAAA,OAAO,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,KAAK,KAAK,KAAK,KAAK,SAAS,CAAC;AAC9D,YAAA,QAAQ,EAAE,WAAW,CAAC,IAAI,CAAC,CAAC,KAAK,KAAK,KAAK,CAAC,QAAQ,KAAK,OAAO,CAAC;SAClE;IACH;AACA,IAAA,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO;IAC9B,IAAI,OAAO,IAAI,qBAAqB,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE;AACjD,QAAA,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE;IACrC;IACA,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,YAAY,EAAE,OAAO,EAAE,YAAY,EAAE;AAChE;AAEA;;;;;;AAMG;AACG,SAAU,sBAAsB,CAAC,IAA0C,EAAA;IAC/E,QAAQ,IAAI;AACV,QAAA,KAAK,OAAO;AACV,YAAA,OAAO,QAAQ;AACjB,QAAA,KAAK,WAAW;AACd,YAAA,OAAO,WAAW;AACpB,QAAA,KAAK,gBAAgB;AACnB,YAAA,OAAO,sBAAsB;AAC/B,QAAA;AACE,YAAA,OAAO,SAAS;;AAEtB;AAEA;;;;;;AAMG;AACG,SAAU,uBAAuB,CACrC,IAA0C,EAAA;IAE1C,QAAQ,IAAI;AACV,QAAA,KAAK,OAAO;AACV,YAAA,OAAO,iBAAiB;AAC1B,QAAA,KAAK,WAAW;AACd,YAAA,OAAO,oBAAoB;AAC7B,QAAA,KAAK,gBAAgB;AACnB,YAAA,OAAO,gCAAgC;AACzC,QAAA;AACE,YAAA,OAAO,SAAS;;AAEtB;AAmBA;AACA,MAAM,UAAU,GAA6B,EAAE,WAAW,EAAE,IAAI,EAAE;AAElE;;;;;;AAMG;AACH,MAAM,oBAAoB,GAAsC;AAC9D,IAAA,KAAK,EAAE,qHAAqH;AAC5H,IAAA,SAAS,EACP,wHAAwH;AAC1H,IAAA,cAAc,EACZ,oIAAoI;CACvI;AAED;;;;;;;;;;;;;;;;;;AAkBG;AACG,SAAU,wBAAwB,CACtC,YAAkD,EAClD,cAA+D,EAAA;AAE/D,IAAA,IAAI,CAAC,YAAY,IAAI,cAAc,IAAI,IAAI,EAAE;AAC3C,QAAA,OAAO,UAAU;IACnB;AACA,IAAA,IAAI,cAAc,CAAC,QAAQ,CAAC,YAAY,CAAC,EAAE;AACzC,QAAA,OAAO,UAAU;IACnB;IACA,MAAM,UAAU,GAAG;SAChB,GAAG,CAAC,CAAC,IAAI,KAAK,sBAAsB,CAAC,IAAI,CAAC;SAC1C,MAAM,CAAC,CAAC,KAAK,KAAsB,KAAK,KAAK,SAAS,CAAC;;AAE1D,IAAA,MAAM,IAAI,GACR,UAAU,CAAC,MAAM,GAAG;UAChB,sBAAsB,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA,CAAA;UAC3C,wDAAwD;AAC9D,IAAA,OAAO,EAAE,WAAW,EAAE,KAAK,EAAE,MAAM,EAAE,oBAAoB,CAAC,YAAY,CAAC,GAAG,IAAI,EAAE;AAClF;AAEA;AACM,SAAU,qBAAqB,CAAC,OAA0B,EAAA;IAC9D,QACE,CAAC,OAAO,CAAC,SAAS,EAAE,MAAM,IAAI,CAAC;AAC/B,SAAC,OAAO,CAAC,SAAS,EAAE,MAAM,IAAI,CAAC,CAAC;SAC/B,OAAO,CAAC,OAAO,EAAE,MAAM,IAAI,CAAC,CAAC;AAElC;;ACtOA;;;;;;;;;;;;;;;;;;;AAmBG;AAaH;AACA,MAAM,oBAAoB,GAAG,GAAG;AAEhC;;;AAGG;AACH,MAAM,cAAc,GAAG,IAAI;MAGd,oBAAoB,CAAA;AACd,IAAA,GAAG,GAAG,MAAM,CAAC,aAAa,CAAC;AAC3B,IAAA,KAAK,GAAG,MAAM,CAAC,gBAAgB,CAAC;AAChC,IAAA,UAAU,GAAG,MAAM,CAAC,UAAU,CAAC;AAE/B,IAAA,aAAa,GAAG,MAAM,CAAC,KAAK,yDAAC;AAC7B,IAAA,YAAY,GAAG,MAAM,CAAC,KAAK,wDAAC;AAC5B,IAAA,MAAM,GAAG,MAAM,CAA0B,SAAS,kDAAC;AACpE;;;;;AAKG;AACc,IAAA,YAAY,GAAG,mBAAmB,CAAC,IAAI,CAAC,GAAG,CAAC;IAC5C,mBAAmB,GAAG,MAAM,CAAC,IAAI,CAAC,YAAY,CAAC,cAAc,EAAA,IAAA,SAAA,GAAA,CAAA,EAAA,SAAA,EAAA,qBAAA,EAAA,CAAA,GAAA,EAAA,CAAA,CAAC;AAC9D,IAAA,SAAS,GAAG,MAAM,CAAC,KAAK,qDAAC;;IAEzB,gBAAgB,GAAG,MAAM,CAAC,IAAI,CAAC,YAAY,CAAC,IAAI,EAAA,IAAA,SAAA,GAAA,CAAA,EAAA,SAAA,EAAA,kBAAA,EAAA,CAAA,GAAA,EAAA,CAAA,CAAC;;AAEjD,IAAA,UAAU,GAAG,MAAM,CAAC,KAAK,sDAAC;AAC1B,IAAA,UAAU,GAAG,MAAM,CAA0B,SAAS,sDAAC;AACxE;;;;AAIG;AACc,IAAA,mBAAmB,GAAG,MAAM,CAA6B,SAAS,+DAAC;AACpF;;;;;;AAMG;AACc,IAAA,mBAAmB,GAAG,MAAM,CAAqB,SAAS,+DAAC;;AAE3D,IAAA,uBAAuB,GAAG,MAAM,CAAC,KAAK,mEAAC;AACxD;;;;;;;;;;;;AAYG;AACc,IAAA,eAAe,GAAG,MAAM,CAA2C,SAAS,2DAAC;AAErF,IAAA,YAAY,GAAG,IAAI,CAAC,aAAa,CAAC,UAAU,EAAE;AAC9C,IAAA,WAAW,GAAG,IAAI,CAAC,YAAY,CAAC,UAAU,EAAE;AAC5C,IAAA,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC,UAAU,EAAE;AAChC,IAAA,kBAAkB,GAAG,IAAI,CAAC,mBAAmB,CAAC,UAAU,EAAE;AAC1D,IAAA,QAAQ,GAAG,IAAI,CAAC,SAAS,CAAC,UAAU,EAAE;AACtC,IAAA,eAAe,GAAG,IAAI,CAAC,gBAAgB,CAAC,UAAU,EAAE;AACpD,IAAA,SAAS,GAAG,IAAI,CAAC,UAAU,CAAC,UAAU,EAAE;AACxC,IAAA,SAAS,GAAG,IAAI,CAAC,UAAU,CAAC,UAAU,EAAE;AACxC,IAAA,kBAAkB,GAAG,IAAI,CAAC,mBAAmB,CAAC,UAAU,EAAE;AAC1D,IAAA,kBAAkB,GAAG,IAAI,CAAC,mBAAmB,CAAC,UAAU,EAAE;AAC1D,IAAA,sBAAsB,GAAG,IAAI,CAAC,uBAAuB,CAAC,UAAU,EAAE;AAClE,IAAA,cAAc,GAAG,IAAI,CAAC,eAAe,CAAC,UAAU,EAAE;;AAGlD,IAAA,SAAS,GAAG,QAAQ,CAAC,MAAM,IAAI,CAAC,KAAK,CAAC,cAAc,EAAE,KAAK,SAAS,qDAAC;AAE9E,IAAA,WAAA,GAAA;AACE,QAAA,YAAY,CAAC,IAAI,CAAC,KAAK,CAAC,UAAU;aAC/B,IAAI;AACH;;;;;;;;;;;AAWG;QACH,MAAM,CAAC,MAAM,IAAI,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC,EACtC,YAAY,CAAC,oBAAoB,CAAC,EAClC,GAAG,CAAC,MAAK;AACP,YAAA,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,IAAI,CAAC;AAC5B,YAAA,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,SAAS,CAAC;AAC5B,QAAA,CAAC,CAAC;;;QAGF,SAAS,CAAC,CAAC,UAAU,KACnB,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC,IAAI,CAChC,GAAG,CAAC,CAAC,MAAM,KAAI;AACb,YAAA,IAAI,CAAC,KAAK,CAAC,mBAAmB,CAAC,MAAM,CAAC;AACtC,YAAA,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,KAAK,CAAC;AAC/B,QAAA,CAAC,CAAC,EACF,UAAU,CAAC,CAAC,KAAc,KAAI;AAC5B,YAAA,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,KAAK,CAAC;AAC7B,YAAA,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC;;;AAGvC,YAAA,OAAO,KAAK;QACd,CAAC,CAAC,CACH,CACF,EACD,kBAAkB,CAAC,IAAI,CAAC,UAAU,CAAC;AAEpC,aAAA,SAAS,EAAE;;;;AAKd,QAAA,IAAI,IAAI,CAAC,YAAY,CAAC,cAAc,EAAE;AACpC,YAAA,IAAI,CAAC;AACF,iBAAA,cAAc;AACd,iBAAA,IAAI,CACH,GAAG,CAAC,CAAC,WAAW,KAAI;gBAClB,IAAI,CAAC,mBAAmB,CAAC,GAAG,CAAC,WAAW,CAAC,OAAO,CAAC;;;;gBAIjD,IAAI,CAAC,mBAAmB,CAAC,GAAG,CAAC,WAAW,CAAC,kBAAkB,CAAC;gBAC5D,IAAI,CAAC,uBAAuB,CAAC,GAAG,CAAC,WAAW,CAAC,sBAAsB,KAAK,IAAI,CAAC;AAC7E;;;;;;AAMG;gBACH,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,WAAW,CAAC,cAAc,IAAI,SAAS,CAAC;AACnE,YAAA,CAAC,CAAC,EACF,UAAU,CAAC,MAAM,KAAK,CAAC,EACvB,kBAAkB,CAAC,IAAI,CAAC,UAAU,CAAC;AAEpC,iBAAA,SAAS,EAAE;QAChB;;;AAIA,QAAA,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC,MAAM,IAAI,CAAC,eAAe,EAAE,CAAC;IACzD;;AAGA,IAAA,cAAc,CAAC,eAAqC,EAAA;AAClD,QAAA,IAAI,CAAC,IAAI,CAAC,mBAAmB,EAAE,EAAE;YAC/B;QACF;AACA,QAAA,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,IAAI,CAAC;AAC3B,QAAA,IAAI,CAAC;aACF,cAAc,CAAC,IAAI,CAAC,KAAK,CAAC,UAAU,EAAE,EAAE,eAAe;AACvD,aAAA,IAAI,CACH,GAAG,CAAC,CAAC,MAAM,KAAI;AACb,YAAA,IAAI,CAAC,KAAK,CAAC,kBAAkB,CAAC,MAAM,CAAC;AACrC,YAAA,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,KAAK,CAAC;AAC9B,QAAA,CAAC,CAAC,EACF,UAAU,CAAC,CAAC,KAAc,KAAI;AAC5B,YAAA,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,KAAK,CAAC;YAC5B,MAAM,QAAQ,GAAG,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC;AACvC,YAAA,IAAI,QAAQ,CAAC,QAAQ,KAAK,gBAAgB,EAAE;AAC1C,gBAAA,IAAI,CAAC,mBAAmB,CAAC,GAAG,CAAC,KAAK,CAAC;YACrC;iBAAO;AACL,gBAAA,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;YAC3B;AACA,YAAA,OAAO,EAAE,CAAC,SAAS,CAAC;QACtB,CAAC,CAAC,EACF,kBAAkB,CAAC,IAAI,CAAC,UAAU,CAAC;AAEpC,aAAA,SAAS,EAAE;IAChB;AAEA;;;;;;;;AAQG;IACH,IAAI,GAAA;QACF,IAAI,CAAC,IAAI,CAAC,gBAAgB,EAAE,IAAI,IAAI,CAAC,SAAS,EAAE,EAAE;YAChD;QACF;QACA,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,UAAU,EAAE;AACpC,QAAA,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC;AACxB,QAAA,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,SAAS,CAAC;AAC9B,QAAA,IAAI,CAAC;aACF,IAAI,CAAC,IAAI;AACT,aAAA,IAAI,CACH,GAAG,CAAC,MAAK;AACP,YAAA,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC;;;AAGzB,YAAA,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,IAAI,CAAC;YAC1B,IAAI,CAAC,cAAc,EAAE;AACvB,QAAA,CAAC,CAAC,EACF,UAAU,CAAC,CAAC,KAAc,KAAI;AAC5B,YAAA,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC;YACzB,MAAM,QAAQ,GAAG,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC;AACvC,YAAA,IAAI,QAAQ,CAAC,QAAQ,KAAK,gBAAgB,EAAE;;;;AAI1C,gBAAA,IAAI,CAAC,gBAAgB,CAAC,GAAG,CAAC,KAAK,CAAC;YAClC;;;AAGA,YAAA,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,QAAQ,CAAC;AAC7B,YAAA,OAAO,EAAE,CAAC,SAAS,CAAC;QACtB,CAAC,CAAC,EACF,kBAAkB,CAAC,IAAI,CAAC,UAAU,CAAC;AAEpC,aAAA,SAAS,EAAE;IAChB;IAEA,YAAY,GAAA;AACV,QAAA,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,SAAS,CAAC;IAC5B;IAEA,gBAAgB,GAAA;AACd,QAAA,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,SAAS,CAAC;IAChC;AAEQ,IAAA,eAAe;IAEf,cAAc,GAAA;QACpB,IAAI,CAAC,eAAe,EAAE;AACtB,QAAA,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC;AACzB,QAAA,IAAI,CAAC,eAAe,GAAG,UAAU,CAAC,MAAK;AACrC,YAAA,IAAI,CAAC,eAAe,GAAG,SAAS;AAChC,YAAA,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,KAAK,CAAC;QAC5B,CAAC,EAAE,cAAc,CAAC;IACpB;IAEQ,eAAe,GAAA;AACrB,QAAA,IAAI,IAAI,CAAC,eAAe,KAAK,SAAS,EAAE;AACtC,YAAA,YAAY,CAAC,IAAI,CAAC,eAAe,CAAC;AAClC,YAAA,IAAI,CAAC,eAAe,GAAG,SAAS;QAClC;AACA,QAAA,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,KAAK,CAAC;IAC5B;AAEA;;;AAGG;AACK,IAAA,UAAU,CAAC,KAAc,EAAA;AAC/B,QAAA,IAAI,WAAW,CAAC,EAAE,CAAC,KAAK,CAAC,EAAE;AACzB,YAAA,OAAO,KAAK;QACd;AACA,QAAA,MAAM,OAAO,GAAG,KAAK,YAAY,KAAK,GAAG,KAAK,CAAC,OAAO,GAAG,8BAA8B;QACvF,OAAO,IAAI,WAAW,CAAC,WAAW,EAAE,OAAO,EAAE,KAAK,CAAC;IACrD;wGA/PW,oBAAoB,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,UAAA,EAAA,CAAA;4GAApB,oBAAoB,EAAA,CAAA;;4FAApB,oBAAoB,EAAA,UAAA,EAAA,CAAA;kBADhC;;;ACzCD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA8BG;AAgBH;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAiCG;AACG,SAAU,0BAA0B,CACxC,cAAsB,EACtB,OAAqC,EAAA;IAErC,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CACvB,CAAC,MAAM;;;AAGL,IAAA,CAAA,EAAG,MAAM,CAAC,QAAQ,CAAA,CAAA,EAAI,MAAM,CAAC;AAC1B,SAAA,GAAG,CAAC,CAAC,MAAM,KAAK,CAAA,EAAG,MAAM,CAAC,IAAI,IAAI,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE;AACpD,SAAA,IAAI,CAAC,GAAG,CAAC,CAAA,CAAE,CACjB;;;IAGD,OAAO,CAAA,EAAG,cAAc,CAAA,MAAA,EAAS,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA,CAAE;AACpD;MAgBa,sBAAsB,CAAA;AAChB,IAAA,GAAG,GAAG,MAAM,CAAC,aAAa,CAAC;AAC3B,IAAA,QAAQ,GAAG,MAAM,CAAC,gBAAgB,CAAC;AACpD;;;;AAIG;AACc,IAAA,UAAU,GAAG,MAAM,CAAC,oBAAoB,CAAC;AAEzC,IAAA,YAAY,GAAG,mBAAmB,CAAC,IAAI,CAAC,GAAG,CAAC;AAE5C,IAAA,OAAO,GAAG,MAAM,CAAuC,IAAI,GAAG,EAAE,mDAAC;AACjE,IAAA,QAAQ,GAAG,MAAM,CAAsB,IAAI,GAAG,EAAE,oDAAC;AAClE;;;;;;;;;;AAUG;AACc,IAAA,OAAO,GAAG,MAAM,CAAqB,SAAS,mDAAC;AAEhE;;;;;AAKG;AACM,IAAA,WAAW,GAAoB,QAAQ,CAC9C,MAAM,IAAI,CAAC,YAAY,CAAC,iBAAiB,IAAI,IAAI,CAAC,UAAU,CAAC,sBAAsB,EAAE,uDACtF;AAED;;;;;;;;AAQG;IACM,SAAS,GAAG,QAAQ,CAAC,MAC5B,0BAA0B,CAAC,IAAI,CAAC,QAAQ,CAAC,UAAU,EAAE,CAAC,IAAI,IAAI,EAAE,EAAE,IAAI,CAAC,QAAQ,CAAC,OAAO,EAAE,CAAC,EAAA,IAAA,SAAA,GAAA,CAAA,EAAA,SAAA,EAAA,WAAA,EAAA,CAAA,GAAA,EAAA,CAAA,CAC3F;AAED;;;;;;;;AAQG;AACc,IAAA,YAAY,GAAG,QAAQ,CAAC,MAAM,IAAI,CAAC,QAAQ,CAAC,cAAc,EAAE,KAAK,SAAS,wDAAC;AAE5F;;;;;;;;;;;;;AAaG;AACH,IAAA,QAAQ,CAAC,QAAgB,EAAA;QACvB,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,EAAE,CAAC,GAAG,CAAC,QAAQ,CAAC;QAC1C,OAAO,KAAK,IAAI,KAAK,CAAC,SAAS,KAAK,IAAI,CAAC,SAAS,EAAE,GAAG,KAAK,CAAC,MAAM,GAAG,SAAS;IACjF;AAEA;;;;AAIG;AACH,IAAA,SAAS,CAAC,QAAgB,EAAA;QACxB,OAAO,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,IAAI,SAAS;IACtD;AAEA;;;;;AAKG;AACH,IAAA,SAAS,CAAC,QAAgB,EAAA;QACxB,OAAO,wBAAwB,CAAC,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;IAC1D;AAEA;;;;;;;AAOG;;AAGH,IAAA,SAAS,CAAC,QAAgB,EAAA;QACxB,OAAO,IAAI,CAAC,QAAQ,EAAE,CAAC,GAAG,CAAC,QAAQ,CAAC;IACtC;AAEA,IAAA,WAAA,GAAA;AACE;;;;;;;;;;;;;;;;;;;AAmBG;QACH,MAAM,CAAC,MAAK;AACV,YAAA,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,EAAE;AAC/B,YAAA,MAAM,SAAS,GAAG,IAAI,CAAC,SAAS,EAAE;AAClC,YAAA,IAAI,CAAC,QAAQ,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,IAAI,CAAC,IAAI,CAAC,YAAY,EAAE,EAAE;gBAC5D;YACF;AACA,YAAA,SAAS,CAAC,MAAM,IAAI,CAAC,aAAa,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC;AAC1D,QAAA,CAAC,CAAC;IACJ;AAEA;;;;;;;;;;;;;;;;;;AAkBG;AACH,IAAA,UAAU,CAAC,QAAgB,EAAA;QACzB,IAAI,CAAC,QAAQ,EAAE;YACb;QACF;AACA,QAAA,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC;QAC1B,SAAS,CAAC,MAAK;YACb,IAAI,IAAI,CAAC,WAAW,EAAE,IAAI,IAAI,CAAC,YAAY,EAAE,EAAE;gBAC7C,IAAI,CAAC,aAAa,CAAC,QAAQ,EAAE,IAAI,CAAC,SAAS,EAAE,CAAC;YAChD;AACF,QAAA,CAAC,CAAC;IACJ;AAEA;;;;;;AAMG;IACK,aAAa,CAAC,QAAgB,EAAE,SAAiB,EAAA;QACvD,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE;YACjC;QACF;QACA,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,EAAE,CAAC,GAAG,CAAC,QAAQ,CAAC;QAC1C,IAAI,KAAK,IAAI,KAAK,CAAC,SAAS,KAAK,SAAS,EAAE;YAC1C;QACF;AACA,QAAA,IAAI,CAAC,KAAK,CAAC,QAAQ,EAAE,SAAS,EAAE,IAAI,CAAC,QAAQ,CAAC,UAAU,EAAE,CAAC;IAC7D;AAEA;;;;AAIG;AACH,IAAA,OAAO,CAAC,QAAgB,EAAA;QACtB,SAAS,CAAC,MAAK;AACb,YAAA,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE;gBACxD;YACF;AACA,YAAA,IAAI,CAAC,KAAK,CAAC,QAAQ,EAAE,IAAI,CAAC,SAAS,EAAE,EAAE,IAAI,CAAC,QAAQ,CAAC,UAAU,EAAE,CAAC;AACpE,QAAA,CAAC,CAAC;IACJ;AAGQ,IAAA,KAAK,CACX,QAAgB,EAChB,SAAiB,EACjB,UAA6D,EAAA;AAE7D,QAAA,IAAI,CAAC,UAAU,CAAC,QAAQ,EAAE,IAAI,CAAC;AAC/B,QAAA,IAAI,CAAC;AACF,aAAA,iBAAiB,CAAC,UAAU,EAAE,QAAQ;AACtC,aAAA,IAAI,CACH,GAAG,CAAC,CAAC,MAAM,KAAI;AACb,YAAA,IAAI,CAAC,UAAU,CAAC,QAAQ,EAAE,KAAK,CAAC;AAChC;;;;;AAKG;YACH,IAAI,CAAC,GAAG,CAAC,QAAQ,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE,CAAC;AACzC;;;;;;;;;AASG;AACH,YAAA,SAAS,CAAC,MAAM,IAAI,CAAC,aAAa,CAAC,QAAQ,EAAE,IAAI,CAAC,SAAS,EAAE,CAAC,CAAC;AACjE,QAAA,CAAC,CAAC,EACF,UAAU,CAAC,MAAK;AACd,YAAA,IAAI,CAAC,UAAU,CAAC,QAAQ,EAAE,KAAK,CAAC;AAChC;;;;;;;;;AASG;AACH,YAAA,OAAO,EAAE,CAAC,SAAS,CAAC;AACtB,QAAA,CAAC,CAAC;AAEH,aAAA,SAAS,EAAE;IAChB;IAEQ,GAAG,CAAC,QAAgB,EAAE,KAAsB,EAAA;QAClD,MAAM,IAAI,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC;AACpC,QAAA,IAAI,CAAC,GAAG,CAAC,QAAQ,EAAE,KAAK,CAAC;AACzB,QAAA,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC;IACxB;IAEQ,UAAU,CAAC,QAAgB,EAAE,OAAgB,EAAA;QACnD,MAAM,IAAI,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC;QACrC,IAAI,OAAO,EAAE;AACX,YAAA,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC;QACpB;aAAO;AACL,YAAA,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC;QACvB;AACA,QAAA,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC;IACzB;wGA9QW,sBAAsB,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,UAAA,EAAA,CAAA;4GAAtB,sBAAsB,EAAA,CAAA;;4FAAtB,sBAAsB,EAAA,UAAA,EAAA,CAAA;kBADlC;;;AC9GD;;;;;;;;;;;;;;AAcG;MAuCU,gBAAgB,CAAA;AAC3B;;;;AAIG;AACH,IAAA,MAAM,CACJ,KAAoC,EACpC,KAAoC,EACpC,OAA0B,EAAA;AAE1B,QAAA,MAAM,MAAM,GAAG,OAAO,EAAE,MAAM,IAAI,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,EAAE,EAAE;AAElD,QAAA,MAAM,KAAK,GAAG,IAAI,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,UAAU,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC;QAC7E,KAAK,CAAC,QAAQ,CAAC;AACb,YAAA,OAAO,EAAE,OAAO,EAAE,SAAS,IAAI,IAAI;AACnC,YAAA,OAAO,EAAE,OAAO,EAAE,cAAc,IAAI,EAAE;AACtC,YAAA,OAAO,EAAE,OAAO,EAAE,cAAc,IAAI,EAAE;AACtC,YAAA,OAAO,EAAE,CAAC;AACV,YAAA,OAAO,EAAE,CAAC;AACX,SAAA,CAAC;QACF,KAAK,CAAC,mBAAmB,CAAC,OAAO,EAAE,CAAC,CAAC;AAErC,QAAA,MAAM,KAAK,GAAG,IAAI,GAAG,EAAU;AAC/B,QAAA,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE;YACxB,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,EAAE,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,CAAC;AACpE,YAAA,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC;QACtB;AAEA,QAAA,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE;;;YAGxB,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE;gBAChD;YACF;AACA,YAAA,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,EAAE,EAAE,EAAE,EAAE,IAAI,CAAC,EAAE,CAAC;QAChD;AAEA,QAAA,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC;AAEnB,QAAA,MAAM,SAAS,GAAG,IAAI,GAAG,EAA6B;AACtD,QAAA,IAAI,IAAI,GAAG,MAAM,CAAC,iBAAiB;AACnC,QAAA,IAAI,IAAI,GAAG,MAAM,CAAC,iBAAiB;AAEnC,QAAA,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE;YACxB,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAA2C;AAC5E,YAAA,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,CAAC,KAAK,SAAS,IAAI,IAAI,CAAC,CAAC,KAAK,SAAS,EAAE;gBACzD;YACF;YACA,MAAM,CAAC,GAAG,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC,KAAK,GAAG,CAAC;YACjC,MAAM,CAAC,GAAG,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC,MAAM,GAAG,CAAC;AAClC,YAAA,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC;YAClC,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC;YACxB,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC;QAC1B;;;AAIA,QAAA,IAAI,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE;AAClD,YAAA,MAAM,MAAM,GAAG,MAAM,CAAC,CAAC,GAAG,IAAI;AAC9B,YAAA,MAAM,MAAM,GAAG,MAAM,CAAC,CAAC,GAAG,IAAI;YAC9B,KAAK,MAAM,CAAC,IAAI,EAAE,QAAQ,CAAC,IAAI,SAAS,EAAE;AACxC,gBAAA,SAAS,CAAC,GAAG,CAAC,IAAI,EAAE;oBAClB,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,GAAG,MAAM,CAAC;oBAClC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,GAAG,MAAM,CAAC;AACnC,iBAAA,CAAC;YACJ;QACF;AAEA,QAAA,OAAO,SAAS;IAClB;wGAtEW,gBAAgB,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,UAAA,EAAA,CAAA;4GAAhB,gBAAgB,EAAA,CAAA;;4FAAhB,gBAAgB,EAAA,UAAA,EAAA,CAAA;kBAD5B;;;ACpDD;;;;;;;;;;;;;;;AAeG;MAcU,gBAAgB,CAAA;AACV,IAAA,GAAG,GAAG,MAAM,CAAC,aAAa,CAAC;AAE3B,IAAA,OAAO,GAAG,MAAM,CAA0B,EAAE,mDAAC;AAC7C,IAAA,gBAAgB,GAAG,MAAM,CAAC,KAAK,4DAAC;AAChC,IAAA,gBAAgB,GAAG,MAAM,CAAC,KAAK,4DAAC;AAChC,IAAA,OAAO,GAAG,MAAM,CAAqC,IAAI,GAAG,EAAE,mDAAC;AAC/D,IAAA,OAAO,GAAG,IAAI,GAAG,EAAU;AAEnC,IAAA,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,UAAU,EAAE;AAClC,IAAA,eAAe,GAAG,IAAI,CAAC,gBAAgB,CAAC,UAAU,EAAE;;IAG7D,UAAU,GAAA;AACR,QAAA,IAAI,IAAI,CAAC,gBAAgB,EAAE,EAAE;YAC3B;QACF;AACA,QAAA,IAAI,CAAC,gBAAgB,CAAC,GAAG,CAAC,IAAI,CAAC;AAC/B,QAAA,IAAI,CAAC,gBAAgB,CAAC,GAAG,CAAC,IAAI,CAAC;AAC/B,QAAA,IAAI,CAAC;AACF,aAAA,SAAS;AACT,aAAA,IAAI,CACH,GAAG,CAAC,CAAC,MAAM,KAAI;AACb,YAAA,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC;AACxB,YAAA,IAAI,CAAC,gBAAgB,CAAC,GAAG,CAAC,KAAK,CAAC;AAClC,QAAA,CAAC,CAAC,EACF,UAAU,CAAC,MAAK;;;AAGd,YAAA,IAAI,CAAC,gBAAgB,CAAC,GAAG,CAAC,KAAK,CAAC;AAChC,YAAA,OAAO,EAAE,CAAC,EAAE,CAAC;AACf,QAAA,CAAC,CAAC;AAEH,aAAA,SAAS,EAAE;IAChB;;AAGA,IAAA,OAAO,CAAC,SAAiB,EAAA;QACvB,OAAO,IAAI,CAAC,OAAO,EAAE,CAAC,GAAG,CAAC,SAAS,CAAC;IACtC;;AAGA,IAAA,QAAQ,CAAC,SAAiB,EAAA;QACxB,IAAI,CAAC,SAAS,IAAI,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE;YAC9E;QACF;AACA,QAAA,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC;AAC3B,QAAA,IAAI,CAAC;aACF,QAAQ,CAAC,SAAS;AAClB,aAAA,IAAI,CACH,GAAG,CAAC,CAAC,MAAM,KAAI;AACb,YAAA,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,SAAS,CAAC;AAC9B,YAAA,IAAI,CAAC,GAAG,CAAC,SAAS,EAAE,EAAE,OAAO,EAAE,MAAM,CAAC,OAAO,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,EAAE,CAAC;AACvE,QAAA,CAAC,CAAC,EACF,UAAU,CAAC,MAAK;AACd,YAAA,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,SAAS,CAAC;;;AAG9B,YAAA,OAAO,EAAE,CAAC,SAAS,CAAC;AACtB,QAAA,CAAC,CAAC;AAEH,aAAA,SAAS,EAAE;IAChB;IAEQ,GAAG,CAAC,SAAiB,EAAE,KAAoB,EAAA;QACjD,MAAM,IAAI,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC;AACpC,QAAA,IAAI,CAAC,GAAG,CAAC,SAAS,EAAE,KAAK,CAAC;AAC1B,QAAA,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC;IACxB;wGApEW,gBAAgB,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,UAAA,EAAA,CAAA;4GAAhB,gBAAgB,EAAA,CAAA;;4FAAhB,gBAAgB,EAAA,UAAA,EAAA,CAAA;kBAD5B;;;AC5BD;;;;;;;;;;;;;;AAcG;AAYG,SAAU,iBAAiB,CAAC,UAAiC,EAAA;AACjE,IAAA,MAAM,QAAQ,GAAG,eAAe,CAAC,UAAU,CAAC;AAC5C,IAAA,IAAI,QAAQ,CAAC,QAAQ,EAAE;AACrB,QAAA,OAAO,QAAQ;IACjB;AACA,IAAA,IAAI,QAAQ,CAAC,MAAM,EAAE;AACnB,QAAA,OAAO,MAAM;IACf;;;AAGA,IAAA,IAAI,QAAQ,CAAC,mBAAmB,EAAE;AAChC,QAAA,OAAO,cAAc;IACvB;AACA,IAAA,OAAO,WAAW;AACpB;AAEA;AACM,SAAU,sBAAsB,CAAC,UAAiC,EAAA;AACtE,IAAA,OAAO,eAAe,iBAAiB,CAAC,UAAU,CAAC,EAAE;AACvD;AAEA;;;AAGG;AACI,MAAM,mBAAmB,GAA+D;;;AAG7F,IAAA,WAAW,EAAE;QACX,qCAAqC;QACrC,wCAAwC;QACxC,oCAAoC;AACrC,KAAA;;IAED,OAAO,EAAE,CAAC,8BAA8B,CAAC;;;;AAIzC,IAAA,KAAK,EAAE,CAAC,8CAA8C,EAAE,+CAA+C,CAAC;;;AAGxG,IAAA,UAAU,EAAE,CAAC,cAAc,EAAE,gBAAgB,EAAE,gBAAgB,CAAC;;IAEhE,UAAU,EAAE,CAAC,SAAS,EAAE,eAAe,EAAE,UAAU,EAAE,iBAAiB,CAAC;;AAEvE,IAAA,OAAO,EAAE,CAAC,cAAc,EAAE,eAAe,CAAC;;AAE1C,IAAA,cAAc,EAAE,CAAC,aAAa,EAAE,SAAS,EAAE,UAAU,EAAE,SAAS,EAAE,UAAU,EAAE,cAAc,EAAE,gBAAgB,CAAC;;AAE/G,IAAA,SAAS,EAAE,CAAC,oBAAoB,EAAE,WAAW,CAAC;;;IAG9C,WAAW,EAAE,CAAC,2DAA2D,CAAC;;AAE1E,IAAA,UAAU,EAAE,CAAC,UAAU,EAAE,cAAc,EAAE,2CAA2C,CAAC;;AAErF,IAAA,gBAAgB,EAAE;QAChB,2BAA2B;QAC3B,gBAAgB;QAChB,2CAA2C;AAC5C,KAAA;;AAGH;;;;AAIG;AACG,SAAU,gBAAgB,CAAC,UAAiC,EAAA;AAChE,IAAA,OAAO,mBAAmB,CAAC,UAAU,CAAC,IAAI,EAAE;AAC9C;;AChGA;;;;;;;;;;AAUG;AAUH;;;;;AAKG;AACG,SAAU,qBAAqB,CAAC,MAAe,EAAE,IAAa,EAAE,MAAM,GAAG,CAAC,EAAA;IAC9E,QACE,MAAM,CAAC,IAAI,IAAI,IAAI,CAAC,IAAI,GAAG,MAAM;AACjC,QAAA,MAAM,CAAC,GAAG,IAAI,IAAI,CAAC,GAAG,GAAG,MAAM;AAC/B,QAAA,MAAM,CAAC,KAAK,IAAI,IAAI,CAAC,KAAK,GAAG,MAAM;QACnC,MAAM,CAAC,MAAM,IAAI,IAAI,CAAC,MAAM,GAAG,MAAM;AAEzC;;ACjCA;;;;;;;;;;;;;AAaG;MA8BU,oBAAoB,CAAA;AACtB,IAAA,UAAU,GAAG,KAAK,CAAC,QAAQ,qDAAyB;AAE1C,IAAA,KAAK,GAAG,QAAQ,CAAC,MAAM,gBAAgB,CAAC,IAAI,CAAC,UAAU,EAAE,CAAC,iDAAC;wGAHnE,oBAAoB,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA;AAApB,IAAA,OAAA,IAAA,GAAA,EAAA,CAAA,oBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,SAAA,EAAA,IAAA,EAAA,oBAAoB,EAAA,YAAA,EAAA,IAAA,EAAA,QAAA,EAAA,eAAA,EAAA,MAAA,EAAA,EAAA,UAAA,EAAA,EAAA,iBAAA,EAAA,YAAA,EAAA,UAAA,EAAA,YAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,IAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,EAAA,QAAA,EAAA,EAAA,EAAA,QAAA,EAnBrB,CAAA;;;;;;;;;;;;;;;;;AAiBT,EAAA,CAAA,EAAA,QAAA,EAAA,IAAA,EAAA,eAAA,EAAA,EAAA,CAAA,uBAAA,CAAA,MAAA,EAAA,CAAA;;4FAEU,oBAAoB,EAAA,UAAA,EAAA,CAAA;kBAvBhC,SAAS;AAAC,YAAA,IAAA,EAAA,CAAA;AACT,oBAAA,QAAQ,EAAE,eAAe;AACzB,oBAAA,UAAU,EAAE,IAAI;oBAChB,eAAe,EAAE,uBAAuB,CAAC,MAAM;AAC/C,oBAAA,QAAQ,EAAE,CAAA;;;;;;;;;;;;;;;;;AAiBT,EAAA,CAAA;AACF,iBAAA;;;AC1CD;;;;;;;AAOG;AAKH;AACO,MAAM,eAAe,GAAG;AAE/B;;;;;;;;AAQG;AACH,MAAM,WAAW,GAAG,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC;AAE7C,MAAM,eAAe,GAAG,WAAW,CAAC,MAAM,GAAG,CAAC;AAE9C,SAAS,UAAU,CAAC,SAAiB,EAAA;AACnC,IAAA,OAAO,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,SAAS,EAAE,CAAC,CAAC,EAAE,eAAe,CAAC;AAC1D;AAEM,SAAU,YAAY,CAAC,SAAiB,EAAA;AAC5C,IAAA,OAAO,WAAW,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC;AAC3C;AAEM,SAAU,iBAAiB,CAAC,SAAiB,EAAA;AACjD,IAAA,OAAO,mBAAmB,UAAU,CAAC,SAAS,CAAC,EAAE;AACnD;;ACtCA;;;;;;;;;;;AAWG;AAkDH;AACA,MAAM,WAAW,GAAqC;AACpD,IAAA,MAAM,EAAE,UAAU;AAClB,IAAA,OAAO,EAAE,UAAU;AACnB,IAAA,SAAS,EAAE,YAAY;AACvB,IAAA,OAAO,EAAE,UAAU;CACpB;AAED;;;;;;;;;AASG;AACH,MAAM,WAAW,GAAG,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,EAAE,EAAE;AAEpC;;;;;;;;;;;;AAYG;AACH,MAAM,iBAAiB,GAAG,IAAI;AAE9B;;;AAGG;AACH,MAAM,aAAa,GAAG,EAAE;MAoBX,kBAAkB,CAAA;AACZ,IAAA,KAAK,GAAG,MAAM,CAAC,gBAAgB,CAAC;;AAEhC,IAAA,IAAI,GAAG,MAAM,CAA0B,UAAU,CAAC;;AAG1D,IAAA,SAAS,GAAG,YAAY,CAAC,cAAc;AAEhD;;;;AAIG;IACM,KAAK,GAAG,iBAAiB;AAEzB,IAAA,UAAU,GAAG,KAAK,CAAC,IAAI,sDAAC;IAExB,UAAU,GAAG,MAAM,EAAU;IAC7B,WAAW,GAAG,MAAM,EAA+D;AAE3E,IAAA,MAAM,GAAG,SAAS,CAAC,QAAQ,CAAC,gBAAgB,CAAC;AAC9D;;;AAGG;AACc,IAAA,IAAI,GAAG,SAAS,CAAC,cAAc,gDAAC;IACzC,aAAa,GAAG,KAAK;AAE7B;;;;;AAKG;AACc,IAAA,oBAAoB,GAAG,MAAM,CAAC,KAAK,gEAAC;AAC5C,IAAA,mBAAmB,GAAG,IAAI,CAAC,oBAAoB,CAAC,UAAU,EAAE;AAE5D,IAAA,KAAK,GAAG,QAAQ,CAAkB,MAAK;QAC9C,MAAM,UAAU,GAAG,IAAI,CAAC,KAAK,CAAC,UAAU,EAAE;QAC1C,MAAM,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,SAAS,EAAE;QACxC,MAAM,WAAW,GAAG,IAAI,CAAC,KAAK,CAAC,iBAAiB,EAAE;QAClD,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,EAAE;QACzC,MAAM,KAAK,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,KAAK,IAAI,CAAC,IAAI,CAAC,CAAC;;;QAIxE,OAAO,WAAW,CAAC,UAAU,CAAC,CAAC,GAAG,CAAC,CAAC,OAAO,KAAI;YAC7C,MAAM,QAAQ,GAAG,eAAe,CAAC,OAAO,CAAC,UAAU,CAAC;AACpD,YAAA,MAAM,KAAK,GAAoB,aAAa,CAAC,OAAO,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,MAAM;gBACnE,EAAE,EAAE,IAAI,CAAC,EAAE;gBACX,IAAI,EAAE,IAAI,CAAC,IAAI;gBACf,KAAK,EAAE,IAAI,CAAC,KAAK;AACjB,gBAAA,KAAK,EACH,IAAI,CAAC,KAAK,KAAK;AACb,uBAAG,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,IAAI;AACtC,sBAAE,CAAA,EAAG,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,IAAI,CAAA,CAAA,EAAI,IAAI,CAAC,KAAK,GAAG,CAAC,CAAA,CAAE;gBAChE,UAAU,EAAE,IAAI,CAAC,UAAU;;;;AAI3B,gBAAA,QAAQ,EAAE,IAAI,CAAC,UAAU,KAAK,SAAS,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,UAAU,CAAC;AACvE,aAAA,CAAC,CAAC;AACH,YAAA,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,IAAI;YAC9B,MAAM,eAAe,GAAG,WAAW,CAAC,GAAG,CAAC,IAAI,CAAC;YAC7C,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC;YAEhC,OAAO;gBACL,IAAI;AACJ,gBAAA,EAAE,EAAE,SAAS,CAAC,IAAI,CAAC;gBACnB,UAAU,EAAE,OAAO,CAAC,UAAU;gBAC9B,aAAa,EAAE,QAAQ,CAAC,KAAK;AAC7B,gBAAA,KAAK,EAAE,OAAO,CAAC,IAAI,CAAC,KAAK,IAAI,IAAI;gBACjC,QAAQ,EAAE,eAAe,CAAC,OAAO,CAAC,UAAU,EAAE,OAAO,CAAC,IAAI,CAAC;AAC3D,gBAAA,QAAQ,EAAE,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE;gBAC/C,KAAK;AACL,gBAAA,eAAe,EAAE,KAAK,CAAC,MAAM,GAAG,CAAC;AACjC,gBAAA,UAAU,EAAE,iBAAiB,CAAC,KAAK,CAAC,MAAM,CAAC;AAC3C,gBAAA,WAAW,EAAE,sBAAsB,CAAC,OAAO,CAAC,UAAU,CAAC;AACvD,gBAAA,YAAY,EAAE,eAAe,CAAC,IAAI,CAAC;AACnC,gBAAA,SAAS,EAAE,CAAC,QAAQ,CAAC,MAAM;AAC3B,gBAAA,QAAQ,EAAE,eAAe,EAAE,QAAQ,IAAI,IAAI;AAC3C,gBAAA,eAAe,EAAE,eAAe,EAAE,WAAW,CAAC,MAAM,IAAI,CAAC;;;;AAIzD,gBAAA,eAAe,EACb,CAAC,eAAe,EAAE,eAAe,IAAI,KAAK,KAAK,MAAM,EAAE,YAAY,KAAK,KAAK;;;;;;;;;;;;;;;;;;gBAkB/E,WAAW,EAAE,CAAC,QAAQ,CAAC,MAAM,IAAI,MAAM,EAAE,SAAS,KAAK,IAAI,GAAG,MAAM,CAAC,OAAO,CAAC,MAAM,GAAG,IAAI;aAC3F;AACH,QAAA,CAAC,CAAC;AACJ,IAAA,CAAC,iDAAC;AAEF;;;;AAIG;IACM,KAAK,GAAG,QAAQ,CAAkB,MACzC,IAAI,CAAC;AACF,SAAA,KAAK;SACL,KAAK,CAAC,MAAM,CAAC,CAAC,IAAI,KAAK,IAAI,CAAC,UAAU;AACtC,SAAA,GAAG,CAAC,CAAC,IAAI,MAAM;QACd,EAAE,EAAE,IAAI,CAAC,EAAE;QACX,QAAQ,EAAE,IAAI,CAAC,UAAU;QACzB,QAAQ,EAAE,IAAI,CAAC,QAAQ;QACvB,IAAI,EAAE,IAAI,CAAC,IAAI;KAChB,CAAC,CAAC,iDACN;AAED,IAAA,WAAA,GAAA;AACE;;;;;;;;;;;;;AAaG;QACH,MAAM,CAAC,MAAK;YACV,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,iBAAiB,EAAE;AAC/C,YAAA,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,EAAE;YACxB,IAAI,CAAC,IAAI,EAAE;gBACT;YACF;AACA,YAAA,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE;;gBAEzB,IAAI,CAAC,cAAc,EAAE;gBACrB;YACF;YACA,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,IAAI,KAAK,SAAS,CAAC,IAAI,CAAC,CAAC,EAAE,EAAE,EAAE,KAAK,CAAC;;;;;YAK/D,IAAI,CAAC,MAAM,EAAE;AACf,QAAA,CAAC,CAAC;IACJ;;;;AAMA;;;;AAIG;AACH,IAAA,UAAU,CAAC,IAAY,EAAA;AACrB,QAAA,MAAM,EAAE,GAAG,SAAS,CAAC,IAAI,CAAC;QAC1B,MAAM,OAAO,GAAG,IAAI,CAAC,eAAe,CAAC,EAAE,CAAC;QACxC,IAAI,CAAC,OAAO,EAAE;YACZ;QACF;QACA,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,aAAa,CAAC,qBAAqB,EAAE;AAC5D,QAAA,IAAI,qBAAqB,CAAC,OAAO,CAAC,qBAAqB,EAAE,EAAE,IAAI,EAAE,aAAa,CAAC,EAAE;YAC/E;QACF;;;QAGA,IAAI,CAAC,MAAM,EAAE,CAAC,iBAAiB,CAAC,EAAE,EAAE,IAAI,CAAC;IAC3C;AAEA;;;;AAIG;AACK,IAAA,eAAe,CAAC,EAAU,EAAA;AAChC,QAAA,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,aAAa,CAAC,gBAAgB,CAAc,kBAAkB,CAAC;QAC1F,KAAK,MAAM,OAAO,IAAI,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE;YAC1C,IAAI,OAAO,CAAC,YAAY,CAAC,gBAAgB,CAAC,KAAK,EAAE,EAAE;AACjD,gBAAA,OAAO,OAAO;YAChB;QACF;AACA,QAAA,OAAO,SAAS;IAClB;;IAGA,WAAW,GAAA;AACT,QAAA,OAAO,IAAI,CAAC,KAAK,EAAE,CAAC,GAAG,CAAC,CAAC,IAAI,MAAM;YACjC,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,KAAK,EAAE,YAAY,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC;AACtC,YAAA,MAAM,EAAE,eAAe;AACxB,SAAA,CAAC,CAAC;IACL;;;;AAMA;;;;AAIG;IACH,eAAe,GAAA;AACb,QAAA,IAAI,IAAI,CAAC,aAAa,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC,MAAM,GAAG,CAAC,EAAE;YACjD;QACF;AACA;;;;;;;AAOG;QACH,MAAM,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,SAAS,EAAE;QACxC,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC,IAAI,CAAC,CAAC,IAAI,KAAK,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,EAAE;YAC1D;QACF;AACA,QAAA,IAAI,CAAC,aAAa,GAAG,IAAI;AACzB,QAAA,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,EAAE;;;;AAI5B,QAAA,IAAI,IAAI,CAAC,yBAAyB,EAAE,EAAE;;;;;AAKpC,YAAA,MAAM,CAAC,mBAAmB,CAAC,KAAK,EAAE,KAAK,CAAC;AACxC,YAAA,IAAI,CAAC,oBAAoB,CAAC,GAAG,CAAC,IAAI,CAAC;YACnC;QACF;QACA,MAAM,CAAC,WAAW,CAAC,WAAW,EAAE,KAAK,EAAE,KAAK,CAAC;IAC/C;;IAGQ,yBAAyB,GAAA;AAC/B,QAAA,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,EAAE;AAC1B,QAAA,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE;AACtB,YAAA,OAAO,KAAK;QACd;AACA,QAAA,IAAI,IAAI,GAAG,MAAM,CAAC,iBAAiB;AACnC,QAAA,IAAI,GAAG,GAAG,MAAM,CAAC,iBAAiB;AAClC,QAAA,IAAI,KAAK,GAAG,MAAM,CAAC,iBAAiB;AACpC,QAAA,IAAI,MAAM,GAAG,MAAM,CAAC,iBAAiB;AACrC,QAAA,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE;YACxB,MAAM,KAAK,GAAG,YAAY,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC;AAC7C,YAAA,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC;AACtC,YAAA,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC;AACpC,YAAA,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC,GAAG,KAAK,CAAC;AAChD,YAAA,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,MAAM,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC,GAAG,eAAe,CAAC;QAC9D;AACA,QAAA,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,aAAa;QACpC,MAAM,cAAc,GAAG,IAAI,CAAC,WAAW,GAAG,WAAW,CAAC,CAAC,GAAG,CAAC;QAC3D,MAAM,eAAe,GAAG,IAAI,CAAC,YAAY,GAAG,WAAW,CAAC,CAAC,GAAG,CAAC;QAC7D,IAAI,cAAc,IAAI,CAAC,IAAI,eAAe,IAAI,CAAC,EAAE;AAC/C,YAAA,OAAO,KAAK;QACd;AACA,QAAA,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CACpB,cAAc,GAAG,IAAI,CAAC,GAAG,CAAC,KAAK,GAAG,IAAI,EAAE,CAAC,CAAC,EAC1C,eAAe,GAAG,IAAI,CAAC,GAAG,CAAC,MAAM,GAAG,GAAG,EAAE,CAAC,CAAC,CAC5C;QACD,OAAO,KAAK,GAAG,iBAAiB;IAClC;IAEA,cAAc,GAAA;AACZ,QAAA,IAAI,CAAC,oBAAoB,CAAC,GAAG,CAAC,IAAI,CAAC;IACrC;IAEA,aAAa,GAAA;AACX,QAAA,IAAI,CAAC,MAAM,EAAE,CAAC,WAAW,CAAC,WAAW,EAAE,IAAI,EAAE,KAAK,CAAC;;AAEnD,QAAA,IAAI,CAAC,oBAAoB,CAAC,GAAG,CAAC,KAAK,CAAC;IACtC;;;;AAMA;;;;AAIG;AACH,IAAA,kBAAkB,CAAC,KAA8C,EAAA;QAC/D,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,IAAI,CAAC,KAAK,CAAC,QAAQ,EAAE;YACzC;QACF;QACA,MAAM,MAAM,GAAG,oBAAoB,CAAC,KAAK,CAAC,QAAQ,CAAC;QACnD,MAAM,MAAM,GAAG,mBAAmB,CAAC,KAAK,CAAC,QAAQ,CAAC;AAClD,QAAA,IAAI,CAAC,MAAM,IAAI,CAAC,MAAM,IAAI,MAAM,KAAK,MAAM,CAAC,QAAQ,EAAE;;;YAGpD;QACF;AACA,QAAA,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,QAAQ,EAAE,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,KAAK,CAAC;IACxE;;AAGA,IAAA,oBAAoB,CAAC,KAMpB,EAAA;AACC,QAAA,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,EAAE;YACtB;QACF;QACA,MAAM,cAAc,GAAG,mBAAmB,CAAC,KAAK,CAAC,gBAAgB,CAAC;QAClE,MAAM,cAAc,GAAG,oBAAoB,CAAC,KAAK,CAAC,gBAAgB,CAAC;AACnE,QAAA,IAAI,CAAC,cAAc,IAAI,CAAC,cAAc,EAAE;YACtC;QACF;AACA,QAAA,IAAI,KAAK,CAAC,QAAQ,KAAK,QAAQ,EAAE;AAC/B,YAAA,MAAM,UAAU,GAAG,KAAK,CAAC,YAAY,GAAG,mBAAmB,CAAC,KAAK,CAAC,YAAY,CAAC,GAAG,SAAS;AAC3F,YAAA,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,cAAc,CAAC,QAAQ,EAAE,cAAc,CAAC,IAAI,EAAE,cAAc,CAAC,KAAK,CAAC;YACzF,IAAI,UAAU,EAAE;AACd,gBAAA,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,UAAU,CAAC,QAAQ,EAAE,UAAU,CAAC,IAAI,EAAE,cAAc,EAAE,UAAU,CAAC,KAAK,CAAC;YAC5F;YACA;QACF;AACA,QAAA,MAAM,UAAU,GAAG,KAAK,CAAC,YAAY,GAAG,oBAAoB,CAAC,KAAK,CAAC,YAAY,CAAC,GAAG,SAAS;QAC5F,IAAI,UAAU,EAAE;AACd,YAAA,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,cAAc,CAAC,QAAQ,EAAE,cAAc,CAAC,IAAI,EAAE,UAAU,EAAE,cAAc,CAAC,KAAK,CAAC;QACpG;IACF;AAEA;;;AAGG;AACH,IAAA,WAAW,CAAC,KAA2E,EAAA;QACrF,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,KAAK,KAAI;YACnC,MAAM,IAAI,GAAG,cAAc,CAAC,KAAK,CAAC,EAAE,CAAC;YACrC,IAAI,IAAI,EAAE;AACR,gBAAA,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,IAAI,EAAE,KAAK,CAAC,QAAQ,EAAE,EAAE,MAAM,EAAE,KAAK,GAAG,CAAC,EAAE,CAAC;YAClE;AACF,QAAA,CAAC,CAAC;IACJ;AAEA;;;;AAIG;AACH,IAAA,iBAAiB,CAAC,KAA4B,EAAA;QAC5C,MAAM,KAAK,GAAa,EAAE;AAC1B,QAAA,KAAK,MAAM,EAAE,IAAI,KAAK,CAAC,OAAO,EAAE;AAC9B,YAAA,MAAM,IAAI,GAAG,cAAc,CAAC,EAAE,CAAC;YAC/B,IAAI,IAAI,EAAE;AACR,gBAAA,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC;YAClB;QACF;AACA,QAAA,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,KAAK,CAAC;IAC9B;AAEA,IAAA,gBAAgB,CAAC,KAA4B,EAAA;AAC3C,QAAA,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,EAAE;YACtB;QACF;AACA,QAAA,KAAK,MAAM,EAAE,IAAI,KAAK,CAAC,OAAO,EAAE;AAC9B,YAAA,MAAM,IAAI,GAAG,cAAc,CAAC,EAAE,CAAC;YAC/B,IAAI,IAAI,EAAE;AACR,gBAAA,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,IAAI,CAAC;YAC7B;QACF;IACF;AAEA;;;;;;;;AAQG;AACH,IAAA,YAAY,CAAC,KAAoE,EAAA;AAC/E,QAAA,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,EAAE;YACtB;QACF;AACA,QAAA,MAAM,UAAU,GAAG,KAAK,CAAC,IAAI;AAC7B,QAAA,IAAI,CAAC,uBAAuB,CAAC,UAAU,CAAC,EAAE;YACxC;QACF;AACA,QAAA,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC;YACpB,UAAU;YACV,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,gBAAgB,CAAC,CAAC,CAAC;YACvC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,gBAAgB,CAAC,CAAC,CAAC;AACxC,SAAA,CAAC;IACJ;AAEA,IAAA,iBAAiB,CAAC,IAAY,EAAA;AAC5B,QAAA,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC;IAC5B;IAEA,WAAW,CAAC,KAAY,EAAE,IAAY,EAAA;;QAEpC,KAAK,CAAC,eAAe,EAAE;AACvB,QAAA,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC;IAC5B;IAEA,aAAa,CAAC,KAAY,EAAE,IAAY,EAAA;QACtC,KAAK,CAAC,eAAe,EAAE;AACvB,QAAA,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,IAAI,CAAC;IAC7B;;AAGA,IAAA,UAAU,CAAC,IAAmB,EAAA;AAC5B,QAAA,OAAO,IAAI,CAAC,KAAK,CAAC,mBAAmB,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC;IACxD;wGA3aW,kBAAkB,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA;AAAlB,IAAA,OAAA,IAAA,GAAA,EAAA,CAAA,oBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,SAAA,EAAA,IAAA,EAAA,kBAAkB,+QAFlB,CAAC,YAAY,CAAC,QAAQ,EAAE,CAAC,CAAC,EAAA,WAAA,EAAA,CAAA,EAAA,YAAA,EAAA,QAAA,EAAA,KAAA,EAAA,IAAA,EAAA,SAAA,EAsBQ,gBAAgB,uFAK3B,cAAc,EAAA,WAAA,EAAA,IAAA,EAAA,QAAA,EAAA,IAAA,EAAA,CAAA,EAAA,QAAA,EAAA,EAAA,EAAA,QAAA,ECjJlD,s0SA+NA,EAAA,YAAA,EAAA,CAAA,EAAA,IAAA,EAAA,UAAA,EAAA,IAAA,EDtHY,WAAW,6tGAAE,oBAAoB,EAAA,QAAA,EAAA,eAAA,EAAA,MAAA,EAAA,CAAA,YAAA,CAAA,EAAA,CAAA,EAAA,eAAA,EAAA,EAAA,CAAA,uBAAA,CAAA,MAAA,EAAA,CAAA;;4FAehC,kBAAkB,EAAA,UAAA,EAAA,CAAA;kBAlB9B,SAAS;AACE,YAAA,IAAA,EAAA,CAAA,EAAA,QAAA,EAAA,YAAY,cACV,IAAI,EAAA,OAAA,EACP,CAAC,WAAW,EAAE,oBAAoB,CAAC,EAAA,eAAA,EAC3B,uBAAuB,CAAC,MAAM,aAYpC,CAAC,YAAY,CAAC,QAAQ,EAAE,CAAC,CAAC,EAAA,QAAA,EAAA,s0SAAA,EAAA;AAsBQ,SAAA,CAAA,EAAA,cAAA,EAAA,MAAA,EAAA,EAAA,cAAA,EAAA,EAAA,UAAA,EAAA,CAAA,EAAA,IAAA,EAAA,EAAA,CAAA,KAAA,EAAA,IAAA,EAAA,CAAA,EAAA,QAAA,EAAA,IAAA,EAAA,KAAA,EAAA,YAAA,EAAA,QAAA,EAAA,KAAA,EAAA,CAAA,EAAA,CAAA,EAAA,UAAA,EAAA,CAAA,EAAA,IAAA,EAAA,EAAA,CAAA,MAAA,EAAA,IAAA,EAAA,CAAA,YAAA,CAAA,EAAA,CAAA,EAAA,WAAA,EAAA,CAAA,EAAA,IAAA,EAAA,EAAA,CAAA,MAAA,EAAA,IAAA,EAAA,CAAA,aAAA,CAAA,EAAA,CAAA,EAAA,MAAA,EAAA,CAAA,EAAA,IAAA,EAAA,EAAA,CAAA,SAAA,EAAA,IAAA,EAAA,CAAA,EAAA,CAAA,UAAA,CAAA,MAAA,gBAAgB,mFAK3B,cAAc,CAAA,EAAA,EAAA,QAAA,EAAA,IAAA,EAAA,CAAA,EAAA,CAAA,EAAA,EAAA,CAAA;;AEjJlD;;;;;;;;;;;;;;;AAeG;MAwFU,4BAA4B,CAAA;AACtB,IAAA,KAAK,GAAG,MAAM,CAAC,gBAAgB,CAAC;;IAGxC,QAAQ,GAAG,MAAM,EAAwC;AAEzD,IAAA,SAAS,GAAG,QAAQ,CAAC,MAAM,IAAI,CAAC,KAAK,CAAC,cAAc,EAAE,KAAK,SAAS,qDAAC;AAErE,IAAA,YAAY,GAAG,QAAQ,CAAC,MAAK;QACpC,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,UAAU,EAAE,CAAC,OAAO;AAC/C,QAAA,OAAO,kBAAkB,CAAC,OAAO,CAAC,IAAI,OAAO;AAC/C,IAAA,CAAC,wDAAC;AAEO,IAAA,IAAI,GAAG,QAAQ,CAAqB,MAAK;QAChD,MAAM,UAAU,GAAG,IAAI,CAAC,KAAK,CAAC,UAAU,EAAE;AAC1C,QAAA,OAAO,IAAI,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC,GAAG,CAAC,CAAC,UAAU,MAAM;YACnD,UAAU;AACV,YAAA,QAAQ,EAAE,0BAA0B,CAAC,UAAU,EAAE,UAAU,CAAC;AAC5D,YAAA,QAAQ,EAAE,qBAAqB,CAAC,UAAU,EAAE,UAAU,CAAC;AACvD,YAAA,KAAK,EAAE,UAAU,CAAC,IAAI,EAAE,KAAK;AAC7B,YAAA,eAAe,EAAE,kBAAkB,CAAC,UAAU,CAAC;AAC/C,YAAA,SAAS,EAAE,sBAAsB,CAAC,UAAU,CAAC;AAC9C,SAAA,CAAC,CAAC;AACL,IAAA,CAAC,gDAAC;AAEO,IAAA,MAAM,GAAG,QAAQ,CAAC,MAAK;QAC9B,IAAI,MAAM,GAAG,CAAC;QACd,IAAI,QAAQ,GAAG,CAAC;QAChB,IAAI,KAAK,GAAG,CAAC;QACb,IAAI,aAAa,GAAG,CAAC;QACrB,KAAK,MAAM,GAAG,IAAI,IAAI,CAAC,IAAI,EAAE,EAAE;AAC7B,YAAA,IAAI,GAAG,CAAC,eAAe,EAAE;gBACvB,aAAa,IAAI,CAAC;YACpB;AACA,YAAA,QAAQ,GAAG,CAAC,UAAU,CAAC,QAAQ;AAC7B,gBAAA,KAAK,OAAO;oBACV,MAAM,IAAI,CAAC;oBACX;AACF,gBAAA,KAAK,SAAS;oBACZ,QAAQ,IAAI,CAAC;oBACb;AACF,gBAAA;oBACE,KAAK,IAAI,CAAC;oBACV;;QAEN;QACA,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,aAAa,EAAE;AACnD,IAAA,CAAC,kDAAC;AAEF;;;;AAIG;AACO,IAAA,UAAU,CAAC,GAAqB,EAAA;AACxC,QAAA,IAAI,CAAC,GAAG,CAAC,QAAQ,EAAE;YACjB;QACF;AACA,QAAA,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,QAAQ,EAAE,GAAG,CAAC,QAAQ,EAAE,KAAK,EAAE,GAAG,CAAC,KAAK,EAAE,CAAC;IAClE;wGA3DW,4BAA4B,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA;AAA5B,IAAA,OAAA,IAAA,GAAA,EAAA,CAAA,oBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,SAAA,EAAA,IAAA,EAAA,4BAA4B,EAAA,YAAA,EAAA,IAAA,EAAA,QAAA,EAAA,uBAAA,EAAA,OAAA,EAAA,EAAA,QAAA,EAAA,UAAA,EAAA,EAAA,QAAA,EAAA,EAAA,EAAA,QAAA,EA5D7B,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA0DT,EAAA,CAAA,EAAA,QAAA,EAAA,IAAA,EAAA,eAAA,EAAA,EAAA,CAAA,uBAAA,CAAA,MAAA,EAAA,CAAA;;4FAEU,4BAA4B,EAAA,UAAA,EAAA,CAAA;kBAhExC,SAAS;AAAC,YAAA,IAAA,EAAA,CAAA;AACT,oBAAA,QAAQ,EAAE,uBAAuB;AACjC,oBAAA,UAAU,EAAE,IAAI;oBAChB,eAAe,EAAE,uBAAuB,CAAC,MAAM;AAC/C,oBAAA,QAAQ,EAAE,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA0DT,EAAA,CAAA;AACF,iBAAA;;;ACtGD;;;;;;;;;;;;;;;;;;;;;;;AAuBG;MAuHU,qBAAqB,CAAA;AACf,IAAA,KAAK,GAAG,MAAM,CAAC,gBAAgB,CAAC;AAEjD;;;;AAIG;AACM,IAAA,QAAQ,GAAG,KAAK,CAAC,QAAQ,mDAAU;AAE5C;;;;;;;;AAQG;IACM,eAAe,GAAG,QAAQ,CACjC,MACE,CAAC,IAAI,CAAC,KAAK,CAAC,iBAAiB,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC,EAAE,eAAe,IAAI,KAAK;AAC9E,QAAA,IAAI,CAAC,KAAK,CAAC,YAAY,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC,EAAE,YAAY,KAAK,KAAK,2DACzE;IAEQ,OAAO,GAAG,QAAQ,CAAC,MAAM,IAAI,CAAC,KAAK,CAAC,YAAY,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC,EAAE,OAAO,IAAI,EAAE,EAAA,IAAA,SAAA,GAAA,CAAA,EAAA,SAAA,EAAA,SAAA,EAAA,CAAA,GAAA,EAAA,CAAA,CAAC;AAEhG;;;;;;;AAOG;IACM,SAAS,GAAG,QAAQ,CAC3B,MAAM,IAAI,CAAC,KAAK,CAAC,YAAY,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC,EAAE,SAAS,KAAK,IAAI,EAAA,IAAA,SAAA,GAAA,CAAA,EAAA,SAAA,EAAA,WAAA,EAAA,CAAA,GAAA,EAAA,CAAA,CACzE;AAED;;;;;;;;;AASG;AACM,IAAA,MAAM,GAAG,QAAQ,CAAC,MAAK;AAC9B,QAAA,MAAM,OAAO,GAAG,WAAW,CAAC,IAAI,CAAC,KAAK,CAAC,UAAU,EAAE,EAAE,IAAI,CAAC,QAAQ,EAAE,CAAC;AACrE,QAAA,OAAO,OAAO,GAAG,eAAe,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC,MAAM,GAAG,KAAK;AACrE,IAAA,CAAC,kDAAC;IAEO,WAAW,GAAG,QAAQ,CAC7B,MAAM,IAAI,CAAC,KAAK,CAAC,iBAAiB,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC,EAAE,WAAW,IAAI,EAAE,EAAA,IAAA,SAAA,GAAA,CAAA,EAAA,SAAA,EAAA,aAAA,EAAA,CAAA,GAAA,EAAA,CAAA,CAC7E;AAED;;;;AAIG;AACO,IAAA,OAAO,CAAC,UAAsB,EAAA;AACtC,QAAA,OAAO,kBAAkB,CAAC,UAAU,CAAC;IACvC;wGAjEW,qBAAqB,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA;AAArB,IAAA,OAAA,IAAA,GAAA,EAAA,CAAA,oBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,SAAA,EAAA,IAAA,EAAA,qBAAqB,EAAA,YAAA,EAAA,IAAA,EAAA,QAAA,EAAA,gBAAA,EAAA,MAAA,EAAA,EAAA,QAAA,EAAA,EAAA,iBAAA,EAAA,UAAA,EAAA,UAAA,EAAA,UAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,IAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,EAAA,QAAA,EAAA,EAAA,EAAA,QAAA,EAzGtB,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAuGT,EAAA,CAAA,EAAA,QAAA,EAAA,IAAA,EAAA,eAAA,EAAA,EAAA,CAAA,uBAAA,CAAA,MAAA,EAAA,CAAA;;4FAEU,qBAAqB,EAAA,UAAA,EAAA,CAAA;kBA7GjC,SAAS;AAAC,YAAA,IAAA,EAAA,CAAA;AACT,oBAAA,QAAQ,EAAE,gBAAgB;AAC1B,oBAAA,UAAU,EAAE,IAAI;oBAChB,eAAe,EAAE,uBAAuB,CAAC,MAAM;AAC/C,oBAAA,QAAQ,EAAE,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAuGT,EAAA,CAAA;AACF,iBAAA;;;AC7ID;;;;;;;;;;;;AAYG;MAQU,uBAAuB,CAAA;AACjB,IAAA,OAAO,GAAG,MAAM,CAAgC,UAAU,CAAC;AAEnE,IAAA,QAAQ,GAAG,KAAK,CAAqC,EAAE,oDAAC;IAEjE,kBAAkB,GAAA;AAChB,QAAA,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,aAAa;AACzC,QAAA,MAAM,KAAK,GAAG,IAAI,CAAC,QAAQ,EAAE;QAC7B,MAAM,UAAU,GAAG,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,SAAS,GAAG,EAAE,GAAG,MAAM,CAAC,KAAK,CAAC;AAC7E,QAAA,IAAI,MAAM,CAAC,KAAK,KAAK,UAAU,EAAE;AAC/B,YAAA,MAAM,CAAC,KAAK,GAAG,UAAU;QAC3B;IACF;wGAZW,uBAAuB,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA;4FAAvB,uBAAuB,EAAA,YAAA,EAAA,IAAA,EAAA,QAAA,EAAA,kBAAA,EAAA,MAAA,EAAA,EAAA,QAAA,EAAA,EAAA,iBAAA,EAAA,UAAA,EAAA,UAAA,EAAA,UAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,EAAA,QAAA,EAAA,EAAA,EAAA,CAAA;;4FAAvB,uBAAuB,EAAA,UAAA,EAAA,CAAA;kBAJnC,SAAS;AAAC,YAAA,IAAA,EAAA,CAAA;AACT,oBAAA,QAAQ,EAAE,kBAAkB;AAC5B,oBAAA,UAAU,EAAE,IAAI;AACjB,iBAAA;;;ACnBD;;;;;;;;;;;;;;;AAeG;MAiBmB,oBAAoB,CAAA;AACrB,IAAA,KAAK,GAAG,MAAM,CAAC,gBAAgB,CAAC;AAE1C,IAAA,QAAQ,GAAG,KAAK,CAAC,QAAQ,mDAAU;AACnC,IAAA,IAAI,GAAG,KAAK,CAAC,QAAQ,+CAAS;;IAM9B,WAAW,GAAG,QAAQ,CAC7B,MAAM,IAAI,CAAC,KAAK,CAAC,iBAAiB,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC,EAAE,WAAW,IAAI,EAAE,EAAA,IAAA,SAAA,GAAA,CAAA,EAAA,SAAA,EAAA,aAAA,EAAA,CAAA,GAAA,EAAA,CAAA,CAC7E;AAED;;;AAGG;IACM,eAAe,GAAG,QAAQ,CACjC,MACE,CAAC,IAAI,CAAC,KAAK,CAAC,iBAAiB,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC,EAAE,eAAe,IAAI,KAAK;AAC9E,QAAA,IAAI,CAAC,KAAK,CAAC,YAAY,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC,EAAE,YAAY,KAAK,KAAK,2DACzE;;IAGQ,aAAa,GAAG,QAAQ,CAC/B,MAAM,IAAI,CAAC,KAAK,CAAC,YAAY,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC,EAAE,OAAO,IAAI,EAAE,EAAA,IAAA,SAAA,GAAA,CAAA,EAAA,SAAA,EAAA,eAAA,EAAA,CAAA,GAAA,EAAA,CAAA,CACpE;AAED;;;;;;;;;;;;;;;AAeG;IACM,YAAY,GAAG,QAAQ,CAC9B,MAAM,IAAI,CAAC,KAAK,CAAC,YAAY,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC,EAAE,YAAY,IAAI,SAAS,EAAA,IAAA,SAAA,GAAA,CAAA,EAAA,SAAA,EAAA,cAAA,EAAA,CAAA,GAAA,EAAA,CAAA,CAChF;AAED;;;;AAIG;AACM,IAAA,YAAY,GAAG,QAAQ,CAA2B,MAAK;AAC9D,QAAA,MAAM,OAAO,GAAG,WAAW,CAAC,IAAI,CAAC,KAAK,CAAC,UAAU,EAAE,EAAE,IAAI,CAAC,QAAQ,EAAE,CAAC;QACrE,IAAI,CAAC,OAAO,EAAE;AACZ,YAAA,OAAO,EAAE;QACX;QACA,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,EAAE;QACzC,MAAM,MAAM,GAAoB,EAAE;AAClC,QAAA,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU;QAC9B,KAAK,MAAM,SAAS,IAAI,uBAAuB,CAAC,OAAO,CAAC,EAAE;YACxD,IAAI,CAAC,SAAS,CAAC,MAAM,IAAI,CAAC,SAAS,CAAC,UAAU,EAAE;gBAC9C;YACF;AACA,YAAA,KAAK,MAAM,MAAM,IAAI,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,UAAU,CAAC,EAAE,OAAO,IAAI,EAAE,EAAE;;;gBAGrE,MAAM,GAAG,GAAG,MAAM,CAAC,IAAI,CAAC,WAAW,EAAE;gBACrC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE;AAClB,oBAAA,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC;AACb,oBAAA,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC;gBACrB;YACF;QACF;AACA,QAAA,OAAO,MAAM;AACf,IAAA,CAAC,wDAAC;;AAGO,IAAA,UAAU,GAAG,QAAQ,CAAC,MAAM,IAAI,CAAC,KAAK,CAAC,UAAU,EAAE,CAAC,UAAU,IAAI,EAAE,sDAAC;AAE9E;;;;;;;;AAQG;AACH,IAAA,QAAQ,CAAC,KAAa,EAAA;AACpB,QAAA,OAAO,IAAI,CAAC,WAAW,EAAE,CAAC,MAAM,CAAC,CAAC,UAAU,KAAK,kBAAkB,CAAC,UAAU,CAAC,KAAK,KAAK,CAAC;IAC5F;AAEA,IAAA,UAAU,CAAC,KAAa,EAAA;QACtB,OAAO,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,MAAM,GAAG,CAAC;IACxC;AAEA;;;;;AAKG;AACH,IAAA,YAAY,CAAC,UAAkB,EAAE,KAAa,EAAE,KAAa,EAAA;QAC3D,OAAO,IAAI,CAAC,WAAW,EAAE,CAAC,MAAM,CAAC,CAAC,UAAU,KAC1C,wBAAwB,CAAC,UAAU,EAAE,UAAU,EAAE,KAAK,EAAE,KAAK,CAAC,CAC/D;IACH;AAEA;;;;;;;;AAQG;AACO,IAAA,QAAQ,CAAC,KAAc,EAAA;AAC/B,QAAA,OAAO,WAAW,CAAC,KAAK,CAAC;IAC3B;;AAGU,IAAA,KAAK,CAAC,KAAqB,EAAA;AACnC,QAAA,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,IAAI,CAAC,UAAU,EAAE,IAAI,CAAC,QAAQ,EAAE,EAAE,KAAc,CAAC;IACzE;AAEA;;;;;;;;AAQG;IACO,WAAW,CACnB,MAAsB,EACtB,UAAuB,EAAA;AAEvB,QAAA,IAAI,CAAC,KAAK,CAAC,oBAAoB,CAAC,IAAI,CAAC,IAAI,EAAE,EAAE,MAAM,EAAE,UAAU,CAAC,CAAC;IACnE;AAEA;;;;;AAKG;IACO,QAAQ,CAAC,KAA2B,EAAE,KAAc,EAAA;QAC5D,MAAM,KAAK,GAA4B,EAAE;AACzC,QAAA,KAAK,CAAC,KAAK,CAAC,GAAG,KAAK,KAAK,EAAE,IAAI,KAAK,KAAK,IAAI,GAAG,SAAS,GAAG,KAAK;AACjE,QAAA,IAAI,CAAC,KAAK,CAAC,KAAuB,CAAC;IACrC;AAEA;;;;;;;;;;;;AAYG;IACO,OAAO,CAAC,KAA2B,EAAE,KAAc,EAAA;QAC3D,MAAM,KAAK,GAA4B,EAAE;AACzC,QAAA,KAAK,CAAC,KAAK,CAAC,GAAG,KAAK,GAAG,IAAI,GAAG,SAAS;AACvC,QAAA,IAAI,CAAC,KAAK,CAAC,KAAuB,CAAC;IACrC;;AAGU,IAAA,QAAQ,CAAC,KAAa,EAAA;AAC9B,QAAA,IAAI,KAAK,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE;AACvB,YAAA,OAAO,SAAS;QAClB;AACA,QAAA,MAAM,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC;AAC5B,QAAA,OAAO,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,GAAG,MAAM,GAAG,SAAS;IACrD;;AAGU,IAAA,OAAO,CAAC,KAAY,EAAA;AAC5B,QAAA,OAAQ,KAAK,CAAC,MAAqE,CAAC,KAAK;IAC3F;AAEU,IAAA,SAAS,CAAC,KAAY,EAAA;AAC9B,QAAA,OAAQ,KAAK,CAAC,MAA2B,CAAC,OAAO;IACnD;wGAhMoB,oBAAoB,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA;4FAApB,oBAAoB,EAAA,YAAA,EAAA,IAAA,EAAA,MAAA,EAAA,EAAA,QAAA,EAAA,EAAA,iBAAA,EAAA,UAAA,EAAA,UAAA,EAAA,UAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,IAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,IAAA,EAAA,EAAA,iBAAA,EAAA,MAAA,EAAA,UAAA,EAAA,MAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,IAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,EAAA,QAAA,EAAA,EAAA,EAAA,CAAA;;4FAApB,oBAAoB,EAAA,UAAA,EAAA,CAAA;kBADzC;;;AC/BD;;;;;;;;;AASG;AAaH,MAAMA,WAAS,GAAiC;IAC9C,OAAO;IACP,eAAe;IACf,KAAK;IACL,KAAK;IACL,KAAK;IACL,KAAK;IACL,QAAQ;IACR,WAAW;IACX,UAAU;IACV,aAAa;CACd;AAwGK,MAAO,8BAA+B,SAAQ,oBAAmC,CAAA;IACnE,UAAU,GAA0B,YAAY;IAE/C,SAAS,GAAGA,WAAS;AAE/B,IAAA,MAAM,GAAG,QAAQ,CAA4B,MAAM,IAAI,CAAC,IAAI,EAAE,CAAC,MAAM,IAAI,EAAE,kDAAC;IAE5E,WAAW,GAAG,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,OAAO,IAAI,EAAE,EAAE,IAAI,CAAC,IAAI,CAAC,EAAA,IAAA,SAAA,GAAA,CAAA,EAAA,SAAA,EAAA,aAAA,EAAA,CAAA,GAAA,EAAA,CAAA,CAAC;AAEpE,IAAA,UAAU,GAAG,QAAQ,CAAC,MAAK;AAClC,QAAA,MAAM,KAAK,GAAG,IAAI,CAAC,YAAY,EAAE,CAAC,GAAG,CAAC,CAAC,MAAM,KAAK,MAAM,CAAC,IAAI,CAAC;AAC9D,QAAA,OAAO,KAAK,CAAC,MAAM,GAAG,EAAE,GAAG,CAAA,EAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC;AACrF,IAAA,CAAC,sDAAC;AAEQ,IAAA,UAAU,CAAC,KAAa,EAAA;AAChC,QAAA,OAAO,KAA0B;IACnC;AAEU,IAAA,SAAS,CAAC,GAAW,EAAA;QAC7B,MAAM,OAAO,GAAG;aACb,KAAK,CAAC,GAAG;aACT,GAAG,CAAC,CAAC,KAAK,KAAK,KAAK,CAAC,IAAI,EAAE;AAC3B,aAAA,MAAM,CAAC,CAAC,KAAK,KAAK,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC;QACtC,IAAI,CAAC,KAAK,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,MAAM,GAAG,CAAC,GAAG,OAAO,GAAG,SAAS,EAAE,CAAC;IACnE;IAEU,QAAQ,GAAA;QAChB,IAAI,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,EAAE,EAAE,QAAQ,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC;IAC9E;AAEU,IAAA,WAAW,CAAC,KAAa,EAAA;QACjC,IAAI,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,QAAQ,KAAK,QAAQ,KAAK,KAAK,CAAC,EAAE,CAAC;IACnF;IAEU,UAAU,CAAC,KAAa,EAAE,KAA8B,EAAA;QAChE,IAAI,CAAC,KAAK,CAAC;AACT,YAAA,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,QAAQ,KACxC,QAAQ,KAAK,KAAK,GAAG,EAAE,GAAG,KAAK,EAAE,GAAG,KAAK,EAAE,GAAG,KAAK,CACpD;AACF,SAAA,CAAC;IACJ;wGAxCW,8BAA8B,EAAA,IAAA,EAAA,IAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA;AAA9B,IAAA,OAAA,IAAA,GAAA,EAAA,CAAA,oBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,SAAA,EAAA,IAAA,EAAA,8BAA8B,EAAA,YAAA,EAAA,IAAA,EAAA,QAAA,EAAA,yBAAA,EAAA,eAAA,EAAA,IAAA,EAAA,QAAA,EAAA,EAAA,EAAA,QAAA,EAjG/B,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA+FT,EAAA,CAAA,EAAA,QAAA,EAAA,IAAA,EAAA,YAAA,EAAA,CAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EAjGS,uBAAuB,EAAA,QAAA,EAAA,kBAAA,EAAA,MAAA,EAAA,CAAA,UAAA,CAAA,EAAA,CAAA,EAAA,eAAA,EAAA,EAAA,CAAA,uBAAA,CAAA,MAAA,EAAA,CAAA;;4FAmGtB,8BAA8B,EAAA,UAAA,EAAA,CAAA;kBAtG1C,SAAS;AAAC,YAAA,IAAA,EAAA,CAAA;AACT,oBAAA,QAAQ,EAAE,yBAAyB;AACnC,oBAAA,UAAU,EAAE,IAAI;oBAChB,OAAO,EAAE,CAAC,uBAAuB,CAAC;oBAClC,eAAe,EAAE,uBAAuB,CAAC,MAAM;AAC/C,oBAAA,QAAQ,EAAE,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA+FT,EAAA,CAAA;AACF,iBAAA;;;ACxID;;;;;;;;;AASG;AAwDG,MAAO,2BAA4B,SAAQ,oBAAgC,CAAA;IAC7D,UAAU,GAA0B,SAAS;AAEtD,IAAA,OAAO,GAAG,QAAQ,CAAoB,MAAM,IAAI,CAAC,IAAI,EAAE,CAAC,OAAO,IAAI,EAAE,mDAAC;IAErE,SAAS,GAAA;;;AAGjB,QAAA,IAAI,CAAC,KAAK,CAAC,EAAE,OAAO,EAAE,CAAC,GAAG,IAAI,CAAC,OAAO,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC;IAClD;AAEU,IAAA,YAAY,CAAC,KAAa,EAAA;QAClC,IAAI,CAAC,KAAK,CAAC,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,QAAQ,KAAK,QAAQ,KAAK,KAAK,CAAC,EAAE,CAAC;IACrF;wGAbW,2BAA2B,EAAA,IAAA,EAAA,IAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA;AAA3B,IAAA,OAAA,IAAA,GAAA,EAAA,CAAA,oBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,SAAA,EAAA,IAAA,EAAA,2BAA2B,EAAA,YAAA,EAAA,IAAA,EAAA,QAAA,EAAA,sBAAA,EAAA,eAAA,EAAA,IAAA,EAAA,QAAA,EAAA,EAAA,EAAA,QAAA,EA7C5B,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA2CT,EAAA,CAAA,EAAA,QAAA,EAAA,IAAA,EAAA,eAAA,EAAA,EAAA,CAAA,uBAAA,CAAA,MAAA,EAAA,CAAA;;4FAEU,2BAA2B,EAAA,UAAA,EAAA,CAAA;kBAjDvC,SAAS;AAAC,YAAA,IAAA,EAAA,CAAA;AACT,oBAAA,QAAQ,EAAE,sBAAsB;AAChC,oBAAA,UAAU,EAAE,IAAI;oBAChB,eAAe,EAAE,uBAAuB,CAAC,MAAM;AAC/C,oBAAA,QAAQ,EAAE,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA2CT,EAAA,CAAA;AACF,iBAAA;;;AChED;;;;;;;;;AASG;AAgIG,MAAO,oCAAqC,SAAQ,oBAAyC,CAAA;IAC/E,UAAU,GAA0B,kBAAkB;AAE/D,IAAA,aAAa,GAAG,QAAQ,CAC/B,MAAM,IAAI,CAAC,IAAI,EAAE,CAAC,aAAa,IAAI,EAAE,yDACtC;AAEQ,IAAA,kBAAkB,GAAG,QAAQ,CAAC,MACrC,CAAC,IAAI,CAAC,KAAK,CAAC,UAAU,EAAE,CAAC,UAAU,IAAI,EAAE,EAAE,MAAM,CAAC,CAAC,SAAS,KAAK,SAAS,CAAC,MAAM,KAAK,UAAU,CAAC,8DAClG;IAES,eAAe,GAAA;AACvB,QAAA,MAAM,YAAY,GAChB,IAAI,CAAC,aAAa,EAAE,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,YAAY,KAAK,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,YAAY,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,GAAG,CAAC;QACjG,IAAI,CAAC,KAAK,CAAC,EAAE,aAAa,EAAE,CAAC,GAAG,IAAI,CAAC,aAAa,EAAE,EAAE,EAAE,QAAQ,EAAE,YAAY,EAAE,CAAC,EAAE,CAAC;IACtF;AAEU,IAAA,kBAAkB,CAAC,KAAa,EAAA;QACxC,IAAI,CAAC,KAAK,CAAC;AACT,YAAA,aAAa,EAAE,IAAI,CAAC,aAAa,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,QAAQ,KAAK,QAAQ,KAAK,KAAK,CAAC;AAChF,SAAA,CAAC;IACJ;IAEU,iBAAiB,CAAC,KAAa,EAAE,KAA2C,EAAA;QACpF,IAAI,CAAC,KAAK,CAAC;AACT,YAAA,aAAa,EAAE,IAAI,CAAC,aAAa,EAAE,CAAC,GAAG,CAAC,CAAC,YAAY,EAAE,QAAQ,KAC7D,QAAQ,KAAK,KAAK,GAAG,EAAE,GAAG,YAAY,EAAE,GAAG,KAAK,EAAE,GAAG,YAAY,CAClE;AACF,SAAA,CAAC;IACJ;wGA7BW,oCAAoC,EAAA,IAAA,EAAA,IAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA;AAApC,IAAA,OAAA,IAAA,GAAA,EAAA,CAAA,oBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,SAAA,EAAA,IAAA,EAAA,oCAAoC,EAAA,YAAA,EAAA,IAAA,EAAA,QAAA,EAAA,gCAAA,EAAA,eAAA,EAAA,IAAA,EAAA,QAAA,EAAA,EAAA,EAAA,QAAA,EA/GrC,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA6GT,EAAA,CAAA,EAAA,QAAA,EAAA,IAAA,EAAA,YAAA,EAAA,CAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EA/GS,uBAAuB,EAAA,QAAA,EAAA,kBAAA,EAAA,MAAA,EAAA,CAAA,UAAA,CAAA,EAAA,CAAA,EAAA,eAAA,EAAA,EAAA,CAAA,uBAAA,CAAA,MAAA,EAAA,CAAA;;4FAiHtB,oCAAoC,EAAA,UAAA,EAAA,CAAA;kBApHhD,SAAS;AAAC,YAAA,IAAA,EAAA,CAAA;AACT,oBAAA,QAAQ,EAAE,gCAAgC;AAC1C,oBAAA,UAAU,EAAE,IAAI;oBAChB,OAAO,EAAE,CAAC,uBAAuB,CAAC;oBAClC,eAAe,EAAE,uBAAuB,CAAC,MAAM;AAC/C,oBAAA,QAAQ,EAAE,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA6GT,EAAA,CAAA;AACF,iBAAA;;;ACxID;;;;;;;;;;;;;;AAcG;AAMH,MAAM,aAAa,GAA2B;IAC5C,QAAQ;IACR,QAAQ;IACR,SAAS;IACT,UAAU;IACV,SAAS;IACT,MAAM;CACP;MA6EY,4BAA4B,CAAA;AAC9B,IAAA,KAAK,GAAG,KAAK,CAAyB,SAAS,iDAAC;AAChD,IAAA,SAAS,GAAG,KAAK,CAAC,UAAU,qDAAC;IAC7B,WAAW,GAAG,MAAM,EAAc;IAExB,KAAK,GAAG,aAAa;AAE/B,IAAA,IAAI,GAAG,QAAQ,CAAc,MAAM,IAAI,CAAC,KAAK,EAAE,EAAE,IAAI,IAAI,QAAQ,gDAAC;IAElE,aAAa,GAAG,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,EAAE,EAAE,aAAa,IAAI,EAAE,EAAE,IAAI,CAAC,IAAI,CAAC,EAAA,IAAA,SAAA,GAAA,CAAA,EAAA,SAAA,EAAA,eAAA,EAAA,CAAA,GAAA,EAAA,CAAA,CAAC;AAEvF;;;;;AAKG;IACM,WAAW,GAAG,QAAQ,CAC7B,MAAM,IAAI,CAAC,IAAI,EAAE,KAAK,QAAQ,IAAI,IAAI,CAAC,KAAK,EAAE,EAAE,MAAM,KAAK,SAAS,EAAA,IAAA,SAAA,GAAA,CAAA,EAAA,SAAA,EAAA,aAAA,EAAA,CAAA,GAAA,EAAA,CAAA,CACrE;IAEQ,cAAc,GAAG,QAAQ,CAChC,MACE,IAAI,CAAC,IAAI,EAAE,KAAK,QAAQ;AACxB,QAAA,IAAI,CAAC,KAAK,EAAE,EAAE,SAAS,KAAK,SAAS;QACrC,IAAI,CAAC,KAAK,EAAE,EAAE,KAAK,KAAK,SAAS,0DACpC;AAEQ,IAAA,kBAAkB,GAAG,QAAQ,CACpC,MAAM,IAAI,CAAC,IAAI,EAAE,KAAK,MAAM,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,EAAE,aAAa,EAAE,MAAM,IAAI,CAAC,IAAI,CAAC,EAAA,IAAA,SAAA,GAAA,CAAA,EAAA,SAAA,EAAA,oBAAA,EAAA,CAAA,GAAA,EAAA,CAAA,CAC/E;AAES,IAAA,MAAM,CAAC,IAAY,EAAA;;;QAG3B,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,IAAmB,EAAE,CAAC;IACtD;IAEU,QAAQ,CAAC,KAAuC,EAAE,GAAW,EAAA;AACrE,QAAA,MAAM,MAAM,GAAG,GAAG,CAAC,IAAI,EAAE,KAAK,EAAE,GAAG,SAAS,GAAG,MAAM,CAAC,GAAG,CAAC;AAC1D,QAAA,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC;AACpB,YAAA,IAAI,IAAI,CAAC,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,EAAE,CAAC;AAC1C,YAAA,CAAC,KAAK,GAAG,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,GAAG,MAAM,GAAG,SAAS;AACtD,SAAA,CAAC;IACJ;AAEU,IAAA,eAAe,CAAC,GAAW,EAAA;QACnC,MAAM,MAAM,GAAG;aACZ,KAAK,CAAC,GAAG;aACT,GAAG,CAAC,CAAC,KAAK,KAAK,KAAK,CAAC,IAAI,EAAE;AAC3B,aAAA,MAAM,CAAC,CAAC,KAAK,KAAK,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC;AACtC,QAAA,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC;YACpB,IAAI,IAAI,CAAC,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;AACrC,YAAA,aAAa,EAAE,MAAM,CAAC,MAAM,GAAG,CAAC,GAAG,MAAM,GAAG,SAAS;AACtD,SAAA,CAAC;IACJ;AAEU,IAAA,IAAI,CAAC,KAAY,EAAA;AACzB,QAAA,OAAQ,KAAK,CAAC,MAA+C,CAAC,KAAK;IACrE;wGA3DW,4BAA4B,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA;AAA5B,IAAA,OAAA,IAAA,GAAA,EAAA,CAAA,oBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,SAAA,EAAA,IAAA,EAAA,4BAA4B,EAAA,YAAA,EAAA,IAAA,EAAA,QAAA,EAAA,wBAAA,EAAA,MAAA,EAAA,EAAA,KAAA,EAAA,EAAA,iBAAA,EAAA,OAAA,EAAA,UAAA,EAAA,OAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,SAAA,EAAA,EAAA,iBAAA,EAAA,WAAA,EAAA,UAAA,EAAA,WAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,EAAA,OAAA,EAAA,EAAA,WAAA,EAAA,aAAA,EAAA,EAAA,QAAA,EAAA,EAAA,EAAA,QAAA,EApE7B,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAkET,EAAA,CAAA,EAAA,QAAA,EAAA,IAAA,EAAA,YAAA,EAAA,CAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EApES,uBAAuB,EAAA,QAAA,EAAA,kBAAA,EAAA,MAAA,EAAA,CAAA,UAAA,CAAA,EAAA,CAAA,EAAA,eAAA,EAAA,EAAA,CAAA,uBAAA,CAAA,MAAA,EAAA,CAAA;;4FAsEtB,4BAA4B,EAAA,UAAA,EAAA,CAAA;kBAzExC,SAAS;AAAC,YAAA,IAAA,EAAA,CAAA;AACT,oBAAA,QAAQ,EAAE,wBAAwB;AAClC,oBAAA,UAAU,EAAE,IAAI;oBAChB,OAAO,EAAE,CAAC,uBAAuB,CAAC;oBAClC,eAAe,EAAE,uBAAuB,CAAC,MAAM;AAC/C,oBAAA,QAAQ,EAAE,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAkET,EAAA,CAAA;AACF,iBAAA;;;ACvGD;;;;;;;;AAQG;AA2IG,MAAO,+BAAgC,SAAQ,oBAAgC,CAAA;IACjE,UAAU,GAA0B,aAAa;AAE1D,IAAA,OAAO,GAAG,QAAQ,CAAoB,MAAM,IAAI,CAAC,IAAI,EAAE,CAAC,OAAO,IAAI,EAAE,mDAAC;AACtE,IAAA,cAAc,GAAG,QAAQ,CAChC,MAAM,IAAI,CAAC,IAAI,EAAE,CAAC,UAAU,IAAI,EAAE,0DACnC;AACQ,IAAA,YAAY,GAAG,QAAQ,CAA2B,MAAM,IAAI,CAAC,IAAI,EAAE,CAAC,YAAY,IAAI,EAAE,wDAAC;IAEtF,SAAS,GAAA;AACjB,QAAA,IAAI,CAAC,KAAK,CAAC,EAAE,OAAO,EAAE,CAAC,GAAG,IAAI,CAAC,OAAO,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC;IAClD;AAEU,IAAA,YAAY,CAAC,KAAa,EAAA;QAClC,IAAI,CAAC,KAAK,CAAC,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,QAAQ,KAAK,QAAQ,KAAK,KAAK,CAAC,EAAE,CAAC;IACrF;IAEU,YAAY,GAAA;QACpB,IAAI,CAAC,KAAK,CAAC,EAAE,UAAU,EAAE,CAAC,GAAG,IAAI,CAAC,cAAc,EAAE,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC;IACjF;AAEU,IAAA,eAAe,CAAC,KAAa,EAAA;QACrC,IAAI,CAAC,KAAK,CAAC;AACT,YAAA,UAAU,EAAE,IAAI,CAAC,cAAc,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,QAAQ,KAAK,QAAQ,KAAK,KAAK,CAAC;AAC9E,SAAA,CAAC;IACJ;IAEU,cAAc,CAAC,KAAa,EAAE,KAAmC,EAAA;QACzE,IAAI,CAAC,KAAK,CAAC;AACT,YAAA,UAAU,EAAE,IAAI,CAAC,cAAc,EAAE,CAAC,GAAG,CAAC,CAAC,SAAS,EAAE,QAAQ,KACxD,QAAQ,KAAK,KAAK,GAAG,EAAE,GAAG,SAAS,EAAE,GAAG,KAAK,EAAE,GAAG,SAAS,CAC5D;AACF,SAAA,CAAC;IACJ;IAEU,SAAS,GAAA;QACjB,IAAI,CAAC,KAAK,CAAC;YACT,YAAY,EAAE,CAAC,GAAG,IAAI,CAAC,YAAY,EAAE,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE,IAAI,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,CAAC;AAC/E,SAAA,CAAC;IACJ;AAEU,IAAA,YAAY,CAAC,KAAa,EAAA;QAClC,IAAI,CAAC,KAAK,CAAC,EAAE,YAAY,EAAE,IAAI,CAAC,YAAY,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,QAAQ,KAAK,QAAQ,KAAK,KAAK,CAAC,EAAE,CAAC;IAC/F;IAEU,WAAW,CAAC,KAAa,EAAE,KAAqD,EAAA;QACxF,IAAI,CAAC,KAAK,CAAC;AACT,YAAA,YAAY,EAAE,IAAI,CAAC,YAAY,EAAE,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,QAAQ,KACpD,QAAQ,KAAK,KAAK,GAAG,EAAE,GAAG,KAAK,EAAE,GAAG,KAAK,EAAE,GAAG,KAAK,CACpD;AACF,SAAA,CAAC;IACJ;wGAnDW,+BAA+B,EAAA,IAAA,EAAA,IAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA;AAA/B,IAAA,OAAA,IAAA,GAAA,EAAA,CAAA,oBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,SAAA,EAAA,IAAA,EAAA,+BAA+B,EAAA,YAAA,EAAA,IAAA,EAAA,QAAA,EAAA,2BAAA,EAAA,eAAA,EAAA,IAAA,EAAA,QAAA,EAAA,EAAA,EAAA,QAAA,EAxHhC,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAsHT,EAAA,CAAA,EAAA,QAAA,EAAA,IAAA,EAAA,YAAA,EAAA,CAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EAxHS,4BAA4B,EAAA,QAAA,EAAA,wBAAA,EAAA,MAAA,EAAA,CAAA,OAAA,EAAA,WAAA,CAAA,EAAA,OAAA,EAAA,CAAA,aAAA,CAAA,EAAA,CAAA,EAAA,eAAA,EAAA,EAAA,CAAA,uBAAA,CAAA,MAAA,EAAA,CAAA;;4FA0H3B,+BAA+B,EAAA,UAAA,EAAA,CAAA;kBA7H3C,SAAS;AAAC,YAAA,IAAA,EAAA,CAAA;AACT,oBAAA,QAAQ,EAAE,2BAA2B;AACrC,oBAAA,UAAU,EAAE,IAAI;oBAChB,OAAO,EAAE,CAAC,4BAA4B,CAAC;oBACvC,eAAe,EAAE,uBAAuB,CAAC,MAAM;AAC/C,oBAAA,QAAQ,EAAE,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAsHT,EAAA,CAAA;AACF,iBAAA;;;AClJD;;;;;;;;;;;;;;;;;AAiBG;AAqBH,MAAMC,OAAK,GAA8B,CAAC,OAAO,EAAE,KAAK,EAAE,iBAAiB,CAAC;AAC5E,MAAMC,YAAU,GAA4B,CAAC,OAAO,EAAE,WAAW,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,WAAW,CAAC;AAiNjG,MAAO,+BAAgC,SAAQ,oBAAoC,CAAA;AACtE,IAAA,QAAQ,GAAG,MAAM,CAAC,gBAAgB,CAAC;IAElC,UAAU,GAA0B,aAAa;IAEhD,KAAK,GAAGD,OAAK;IACb,UAAU,GAAGC,YAAU;AAEjC,IAAA,MAAM,GAAG,QAAQ,CAAC,MAAM,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,kDAAC;AAE/C,IAAA,MAAM,GAAG,QAAQ,CAA6B,MAAM,IAAI,CAAC,IAAI,EAAE,CAAC,MAAM,IAAI,EAAE,kDAAC;AAEtF;;;;;AAKG;IACM,KAAK,GAAG,kBAAkB,CAAC,IAAI,CAAC,IAAI,EAAE,0BAA0B,CAAC;AAE1E;;;;;;;;;;AAUG;AACM,IAAA,UAAU,GAAG,QAAQ,CAC5B,MACE,IAAI,CAAC,IAAI,EAAE,CAAC,IAAI,KAAK,OAAO;QAC5B,IAAI,CAAC,MAAM,EAAE,CAAC,IAAI,CAAC,CAAC,KAAK,KAAK,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC,EAAA,IAAA,SAAA,GAAA,CAAA,EAAA,SAAA,EAAA,YAAA,EAAA,CAAA,GAAA,EAAA,CAAA,CACnE;AAEQ,IAAA,cAAc,GAAG,QAAQ,CAAC,MAAK;QACtC,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC,SAAS;QACvC,IAAI,CAAC,SAAS,EAAE;AACd,YAAA,OAAO,EAAE;QACX;AACA,QAAA,OAAO,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,MAAM,IAAI,EAAE;AAC9D,IAAA,CAAC,0DAAC;;AAGO,IAAA,SAAS,GAAG,QAAQ,CAAC,MAAK;QACjC,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC,SAAS;QACvC,IAAI,CAAC,SAAS,EAAE;AACd,YAAA,OAAO,4EAA4E;QACrF;QACA,MAAM,KAAK,GAAG,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,SAAS,CAAC;QAC9C,IAAI,CAAC,KAAK,EAAE;AACV,YAAA,OAAO,gCAAgC;QACzC;AACA,QAAA,QAAQ,KAAK,CAAC,OAAO;AACnB,YAAA,KAAK,UAAU;gBACb,OAAO,CAAA,oBAAA,EAAuB,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,IAAI,CAAC,CAAA,YAAA,CAAc;AAC9E,YAAA,KAAK,uBAAuB;AAC1B,gBAAA,OAAO,0FAA0F;AACnG,YAAA,KAAK,SAAS;AACZ,gBAAA,OAAO,gEAAgE;AACzE,YAAA;AACE,gBAAA,OAAO,EAAE;;AAEf,IAAA,CAAC,qDAAC;AAEF,IAAA,WAAA,GAAA;AACE,QAAA,KAAK,EAAE;AACP,QAAA,IAAI,CAAC,QAAQ,CAAC,UAAU,EAAE;;;QAG1B,MAAM,CAAC,MAAK;YACV,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC,SAAS;YACvC,IAAI,SAAS,EAAE;AACb,gBAAA,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,SAAS,CAAC;YACnC;AACF,QAAA,CAAC,CAAC;IACJ;IAEU,QAAQ,GAAA;QAChB,IAAI,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC;IAC1D;AAEU,IAAA,WAAW,CAAC,KAAa,EAAA;QACjC,IAAI,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,QAAQ,KAAK,QAAQ,KAAK,KAAK,CAAC,EAAE,CAAC;IACnF;IAEU,UAAU,CAClB,KAAa,EACb,KAA+D,EAAA;QAE/D,IAAI,CAAC,KAAK,CAAC;AACT,YAAA,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,QAAQ,KACxC,QAAQ,KAAK,KAAK,GAAG,EAAE,GAAG,KAAK,EAAE,GAAG,KAAK,EAAE,GAAG,KAAK,CACpD;AACF,SAAA,CAAC;IACJ;AAEU,IAAA,QAAQ,CAAC,KAA8E,EAAA;AAC/F,QAAA,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC,UAAU,IAAI,EAAE,QAAQ,EAAE,EAAE,EAAE;QAC1D,IAAI,CAAC,KAAK,CAAC;AACT,YAAA,UAAU,EAAE,EAAE,GAAG,OAAO,EAAE,GAAI,KAAiC,EAAE;AAClE,SAAA,CAAC;IACJ;AAEA;;;;AAIG;IACO,gBAAgB,GAAA;QACxB,IAAI,CAAC,KAAK,CAAC,EAAE,UAAU,EAAE,SAAS,EAAE,CAAC;IACvC;wGAjHW,+BAA+B,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA;AAA/B,IAAA,OAAA,IAAA,GAAA,EAAA,CAAA,oBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,SAAA,EAAA,IAAA,EAAA,+BAA+B,EAAA,YAAA,EAAA,IAAA,EAAA,QAAA,EAAA,2BAAA,EAAA,eAAA,EAAA,IAAA,EAAA,QAAA,EAAA,EAAA,EAAA,QAAA,EA1MhC,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwMT,EAAA,QAAA,EAAA,IAAA,EAAA,YAAA,EAAA,CAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EA1MS,4BAA4B,6HAAE,uBAAuB,EAAA,QAAA,EAAA,kBAAA,EAAA,MAAA,EAAA,CAAA,UAAA,CAAA,EAAA,CAAA,EAAA,eAAA,EAAA,EAAA,CAAA,uBAAA,CAAA,MAAA,EAAA,CAAA;;4FA4MpD,+BAA+B,EAAA,UAAA,EAAA,CAAA;kBA/M3C,SAAS;AAAC,YAAA,IAAA,EAAA,CAAA;AACT,oBAAA,QAAQ,EAAE,2BAA2B;AACrC,oBAAA,UAAU,EAAE,IAAI;AAChB,oBAAA,OAAO,EAAE,CAAC,4BAA4B,EAAE,uBAAuB,CAAC;oBAChE,eAAe,EAAE,uBAAuB,CAAC,MAAM;AAC/C,oBAAA,QAAQ,EAAE,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAwMT,EAAA,CAAA;AACF,iBAAA;;;ACvPD;;;;;;;;;;;;;AAaG;AAiBH,MAAM,SAAS,GAA8B;IAC3C,QAAQ;IACR,WAAW;IACX,aAAa;IACb,oBAAoB;IACpB,UAAU;IACV,iBAAiB;IACjB,YAAY;IACZ,UAAU;IACV,UAAU;IACV,gBAAgB;IAChB,QAAQ;IACR,WAAW;IACX,IAAI;IACJ,OAAO;CACR;AAED;AACA,MAAM,KAAK,GAA8B,CAAC,QAAQ,EAAE,WAAW,CAAC;AA8K1D,MAAO,2BAA4B,SAAQ,oBAAgC,CAAA;IAC7D,UAAU,GAA0B,SAAS;IAE5C,SAAS,GAAG,SAAS;AAE/B,IAAA,QAAQ,GAAG,QAAQ,CAA6B,MAAM,IAAI,CAAC,IAAI,EAAE,CAAC,QAAQ,IAAI,EAAE,oDAAC;AAE1F;;;;;;AAMG;IACM,KAAK,GAAG,kBAAkB,CAAC,IAAI,CAAC,IAAI,EAAE,qBAAqB,CAAC;IAE5D,gBAAgB,GAAG,QAAQ,CAAC,MACnC,IAAI,CAAC,UAAU,EAAE,CAAC,MAAM,CAAC,CAAC,SAAS,KAAK,SAAS,CAAC,IAAI,KAAK,QAAQ,CAAC,EAAA,IAAA,SAAA,GAAA,CAAA,EAAA,SAAA,EAAA,kBAAA,EAAA,CAAA,GAAA,EAAA,CAAA,CACrE;IAEQ,eAAe,GAAG,QAAQ,CAAC,MAClC,IAAI,CAAC,UAAU,EAAE,CAAC,MAAM,CAAC,CAAC,SAAS,KAAK,SAAS,CAAC,IAAI,KAAK,OAAO,CAAC,EAAA,IAAA,SAAA,GAAA,CAAA,EAAA,SAAA,EAAA,iBAAA,EAAA,CAAA,GAAA,EAAA,CAAA,CACpE;AAES,IAAA,OAAO,CAAC,QAAwB,EAAA;AACxC,QAAA,OAAO,KAAK,CAAC,QAAQ,CAAC,QAAQ,CAAC;IACjC;AAEU,IAAA,QAAQ,CAAC,KAAa,EAAA;AAC9B,QAAA,OAAO,KAAuB;IAChC;AAEU,IAAA,MAAM,CAAC,KAAa,EAAA;;;AAG5B,QAAA,MAAM,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC;QAC5B,OAAO,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,MAAM,IAAI,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC;IACxE;IAEU,YAAY,GAAA;AACpB,QAAA,MAAM,YAAY,GAChB,IAAI,CAAC,QAAQ,EAAE,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,SAAS,KAAK,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,SAAS,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,GAAG,CAAC;QACtF,IAAI,CAAC,KAAK,CAAC;AACT,YAAA,QAAQ,EAAE;gBACR,GAAG,IAAI,CAAC,QAAQ,EAAE;gBAClB,EAAE,QAAQ,EAAE,YAAY,EAAE,eAAe,EAAE,EAAE,EAAE,QAAQ,EAAE,QAAQ,EAAE;AACpE,aAAA;AACF,SAAA,CAAC;IACJ;AAEU,IAAA,eAAe,CAAC,KAAa,EAAA;QACrC,IAAI,CAAC,KAAK,CAAC,EAAE,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,QAAQ,KAAK,QAAQ,KAAK,KAAK,CAAC,EAAE,CAAC;IACvF;IAEU,cAAc,CAAC,KAAa,EAAE,KAA+B,EAAA;QACrE,IAAI,CAAC,KAAK,CAAC;AACT,YAAA,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAE,CAAC,GAAG,CAAC,CAAC,SAAS,EAAE,QAAQ,KAChD,QAAQ,KAAK,KAAK,GAAG,EAAE,GAAG,SAAS,EAAE,GAAG,KAAK,EAAE,GAAG,SAAS,CAC5D;AACF,SAAA,CAAC;IACJ;;IAGU,eAAe,CAAC,KAAa,EAAE,IAAY,EAAA;AACnD,QAAA,IAAI,CAAC,cAAc,CAAC,KAAK,EAAE;YACzB,aAAa,EAAE,IAAI,KAAK,EAAE,GAAG,SAAS,GAAG,IAAI;AAC7C,YAAA,KAAK,EAAE,IAAI,KAAK,EAAE,GAAG,IAAI,CAAC,QAAQ,EAAE,CAAC,KAAK,CAAC,EAAE,KAAK,GAAG,SAAS;AAC/D,SAAA,CAAC;IACJ;wGApEW,2BAA2B,EAAA,IAAA,EAAA,IAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA;AAA3B,IAAA,OAAA,IAAA,GAAA,EAAA,CAAA,oBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,SAAA,EAAA,IAAA,EAAA,2BAA2B,EAAA,YAAA,EAAA,IAAA,EAAA,QAAA,EAAA,sBAAA,EAAA,eAAA,EAAA,IAAA,EAAA,QAAA,EAAA,EAAA,EAAA,QAAA,EAvK5B,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAqKT,EAAA,CAAA,EAAA,QAAA,EAAA,IAAA,EAAA,YAAA,EAAA,CAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EAvKS,uBAAuB,EAAA,QAAA,EAAA,kBAAA,EAAA,MAAA,EAAA,CAAA,UAAA,CAAA,EAAA,CAAA,EAAA,eAAA,EAAA,EAAA,CAAA,uBAAA,CAAA,MAAA,EAAA,CAAA;;4FAyKtB,2BAA2B,EAAA,UAAA,EAAA,CAAA;kBA5KvC,SAAS;AAAC,YAAA,IAAA,EAAA,CAAA;AACT,oBAAA,QAAQ,EAAE,sBAAsB;AAChC,oBAAA,UAAU,EAAE,IAAI;oBAChB,OAAO,EAAE,CAAC,uBAAuB,CAAC;oBAClC,eAAe,EAAE,uBAAuB,CAAC,MAAM;AAC/C,oBAAA,QAAQ,EAAE,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAqKT,EAAA,CAAA;AACF,iBAAA;;;AC7ND;;;;;;;AAOG;AAkBH,MAAM,YAAY,GAAkC;IAClD,MAAM;IACN,aAAa;IACb,WAAW;IACX,UAAU;IACV,cAAc;IACd,YAAY;IACZ,mBAAmB;IACnB,iBAAiB;IACjB,gBAAgB;CACjB;AAED,MAAM,MAAM,GAAiC,CAAC,MAAM,EAAE,UAAU,EAAE,gBAAgB,CAAC;AACnF,MAAM,UAAU,GAAgC,CAAC,MAAM,EAAE,QAAQ,EAAE,YAAY,CAAC;AAChF,MAAMF,WAAS,GAAiC,CAAC,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,OAAO,CAAC;AACrF,MAAM,UAAU,GAAwB;IACtC,QAAQ;IACR,QAAQ;IACR,MAAM;IACN,KAAK;IACL,MAAM;IACN,OAAO;IACP,SAAS;IACT,MAAM;CACP;AAuMK,MAAO,6BAA8B,SAAQ,oBAAkC,CAAA;IACjE,UAAU,GAA0B,WAAW;IAE9C,WAAW,GAAG,YAAY;IAC1B,MAAM,GAAG,MAAM;IACf,UAAU,GAAG,UAAU;IACvB,SAAS,GAAGA,WAAS;IACrB,SAAS,GAAG,UAAU;AAEhC,IAAA,iBAAiB,GAAG,QAAQ,CACnC,MAAM,IAAI,CAAC,IAAI,EAAE,CAAC,iBAAiB,IAAI,EAAE,6DAC1C;AAEQ,IAAA,WAAW,GAAG,QAAQ,CAAgC,MAAM,IAAI,CAAC,IAAI,EAAE,CAAC,WAAW,IAAI,EAAE,uDAAC;AAEzF,IAAA,UAAU,CAAC,KAAa,EAAA;AAChC,QAAA,OAAO,KAA0B;IACnC;AAEU,IAAA,UAAU,CAAC,KAAa,EAAA;QAChC,OAAO,KAAK,KAAK,EAAE,GAAG,SAAS,GAAI,KAAkB;IACvD;IAEU,cAAc,GAAA;QACtB,IAAI,CAAC,KAAK,CAAC;AACT,YAAA,iBAAiB,EAAE;gBACjB,GAAG,IAAI,CAAC,iBAAiB,EAAE;gBAC3B,EAAE,SAAS,EAAE,EAAE,EAAE,QAAQ,EAAE,KAAK,EAAE,WAAW,EAAE,EAAE,EAAE;AACpD,aAAA;AACF,SAAA,CAAC;IACJ;AAEU,IAAA,iBAAiB,CAAC,KAAa,EAAA;QACvC,IAAI,CAAC,KAAK,CAAC;AACT,YAAA,iBAAiB,EAAE,IAAI,CAAC,iBAAiB,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,QAAQ,KAAK,QAAQ,KAAK,KAAK,CAAC;AACxF,SAAA,CAAC;IACJ;IAEU,gBAAgB,CAAC,KAAa,EAAE,KAAwC,EAAA;QAChF,IAAI,CAAC,KAAK,CAAC;AACT,YAAA,iBAAiB,EAAE,IAAI,CAAC,iBAAiB,EAAE,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,QAAQ,KAC9D,QAAQ,KAAK,KAAK,GAAG,EAAE,GAAG,KAAK,EAAE,GAAG,KAAK,EAAE,GAAG,KAAK,CACpD;AACF,SAAA,CAAC;IACJ;IAEU,QAAQ,GAAA;QAChB,IAAI,CAAC,KAAK,CAAC,EAAE,WAAW,EAAE,CAAC,GAAG,IAAI,CAAC,WAAW,EAAE,EAAE,EAAE,SAAS,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC;IACzE;AAEU,IAAA,WAAW,CAAC,KAAa,EAAA;QACjC,IAAI,CAAC,KAAK,CAAC,EAAE,WAAW,EAAE,IAAI,CAAC,WAAW,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,QAAQ,KAAK,QAAQ,KAAK,KAAK,CAAC,EAAE,CAAC;IAC7F;IAEU,UAAU,CAAC,KAAa,EAAE,KAAkC,EAAA;QACpE,IAAI,CAAC,KAAK,CAAC;AACT,YAAA,WAAW,EAAE,IAAI,CAAC,WAAW,EAAE,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,QAAQ,KAClD,QAAQ,KAAK,KAAK,GAAG,EAAE,GAAG,KAAK,EAAE,GAAG,KAAK,EAAE,GAAG,KAAK,CACpD;AACF,SAAA,CAAC;IACJ;wGA5DW,6BAA6B,EAAA,IAAA,EAAA,IAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA;AAA7B,IAAA,OAAA,IAAA,GAAA,EAAA,CAAA,oBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,SAAA,EAAA,IAAA,EAAA,6BAA6B,EAAA,YAAA,EAAA,IAAA,EAAA,QAAA,EAAA,wBAAA,EAAA,eAAA,EAAA,IAAA,EAAA,QAAA,EAAA,EAAA,EAAA,QAAA,EAhM9B,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA8LT,EAAA,CAAA,EAAA,QAAA,EAAA,IAAA,EAAA,YAAA,EAAA,CAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EAhMS,uBAAuB,EAAA,QAAA,EAAA,kBAAA,EAAA,MAAA,EAAA,CAAA,UAAA,CAAA,EAAA,CAAA,EAAA,eAAA,EAAA,EAAA,CAAA,uBAAA,CAAA,MAAA,EAAA,CAAA;;4FAkMtB,6BAA6B,EAAA,UAAA,EAAA,CAAA;kBArMzC,SAAS;AAAC,YAAA,IAAA,EAAA,CAAA;AACT,oBAAA,QAAQ,EAAE,wBAAwB;AAClC,oBAAA,UAAU,EAAE,IAAI;oBAChB,OAAO,EAAE,CAAC,uBAAuB,CAAC;oBAClC,eAAe,EAAE,uBAAuB,CAAC,MAAM;AAC/C,oBAAA,QAAQ,EAAE,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA8LT,EAAA,CAAA;AACF,iBAAA;;;ACvPD;;;;;;;;;AASG;AAgBH,MAAM,SAAS,GAAiC;IAC9C,OAAO;IACP,eAAe;IACf,KAAK;IACL,KAAK;IACL,KAAK;IACL,KAAK;CACN;AA8JK,MAAO,kCAAmC,SAAQ,oBAAuC,CAAA;IAC3E,UAAU,GAA0B,gBAAgB;IAEnD,SAAS,GAAG,SAAS;AAE/B,IAAA,MAAM,GAAG,QAAQ,CAA4B,MAAM,IAAI,CAAC,IAAI,EAAE,CAAC,eAAe,IAAI,EAAE,kDAAC;;IAGrF,KAAK,GAAG,kBAAkB,CAAC,IAAI,CAAC,IAAI,EAAE,6BAA6B,CAAC;AAEnE,IAAA,UAAU,CAAC,KAAa,EAAA;AAChC,QAAA,OAAO,KAA0B;IACnC;IAEU,QAAQ,GAAA;QAChB,IAAI,CAAC,KAAK,CAAC,EAAE,eAAe,EAAE,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,EAAE,EAAE,QAAQ,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC;IACrF;AAEU,IAAA,WAAW,CAAC,KAAa,EAAA;QACjC,IAAI,CAAC,KAAK,CAAC,EAAE,eAAe,EAAE,IAAI,CAAC,MAAM,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,QAAQ,KAAK,QAAQ,KAAK,KAAK,CAAC,EAAE,CAAC;IAC5F;IAEU,UAAU,CAAC,KAAa,EAAE,KAA8B,EAAA;QAChE,IAAI,CAAC,KAAK,CAAC;AACT,YAAA,eAAe,EAAE,IAAI,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,QAAQ,KACjD,QAAQ,KAAK,KAAK,GAAG,EAAE,GAAG,KAAK,EAAE,GAAG,KAAK,EAAE,GAAG,KAAK,CACpD;AACF,SAAA,CAAC;IACJ;wGA5BW,kCAAkC,EAAA,IAAA,EAAA,IAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA;AAAlC,IAAA,OAAA,IAAA,GAAA,EAAA,CAAA,oBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,SAAA,EAAA,IAAA,EAAA,kCAAkC,EAAA,YAAA,EAAA,IAAA,EAAA,QAAA,EAAA,8BAAA,EAAA,eAAA,EAAA,IAAA,EAAA,QAAA,EAAA,EAAA,EAAA,QAAA,EAxJnC,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAsJT,EAAA,CAAA,EAAA,QAAA,EAAA,IAAA,EAAA,eAAA,EAAA,EAAA,CAAA,uBAAA,CAAA,MAAA,EAAA,CAAA;;4FAEU,kCAAkC,EAAA,UAAA,EAAA,CAAA;kBA5J9C,SAAS;AAAC,YAAA,IAAA,EAAA,CAAA;AACT,oBAAA,QAAQ,EAAE,8BAA8B;AACxC,oBAAA,UAAU,EAAE,IAAI;oBAChB,eAAe,EAAE,uBAAuB,CAAC,MAAM;AAC/C,oBAAA,QAAQ,EAAE,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAsJT,EAAA,CAAA;AACF,iBAAA;;;AC7LD;;;;;;;;;;AAUG;AAkBH,MAAMC,OAAK,GAAwB,CAAC,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,WAAW,EAAE,QAAQ,CAAC;AAsNxF,MAAO,yBAA0B,SAAQ,oBAA8B,CAAA;IACzD,UAAU,GAA0B,OAAO;IAE1C,KAAK,GAAGA,OAAK;AAEvB,IAAA,IAAI,GAAG,QAAQ,CAAqB,MAAM,IAAI,CAAC,IAAI,EAAE,CAAC,IAAI,IAAI,EAAE,gDAAC;AACjE,IAAA,MAAM,GAAG,QAAQ,CAA6B,MAAM,IAAI,CAAC,IAAI,EAAE,CAAC,MAAM,IAAI,EAAE,kDAAC;AAEtF;;;;AAIG;IACM,KAAK,GAAG,kBAAkB,CAAC,IAAI,CAAC,IAAI,EAAE,mBAAmB,CAAC;;AAG1D,IAAA,cAAc,GAAG,QAAQ,CAChC,MAAM,IAAI,CAAC,KAAK,CAAC,YAAY,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,iBAAiB,IAAI,EAAE,CAAC,EAAE,OAAO,IAAI,EAAE,0DACxF;AAEQ,IAAA,gBAAgB,GAAG,QAAQ,CAClC,MAAM,IAAI,CAAC,KAAK,CAAC,YAAY,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,mBAAmB,IAAI,EAAE,CAAC,EAAE,OAAO,IAAI,EAAE,4DAC1F;;AAGS,IAAA,eAAe,CAAC,KAAsB,EAAA;QAC9C,OAAO,KAAK,CAAC,UAAU,KAAK,IAAI,CAAC,IAAI,EAAE,CAAC;AACtC,cAAE;cACA,sBAAsB;IAC5B;IAEU,MAAM,GAAA;QACd,IAAI,CAAC,KAAK,CAAC,EAAE,IAAI,EAAE,CAAC,GAAG,IAAI,CAAC,IAAI,EAAE,EAAE,EAAE,gBAAgB,EAAE,EAAE,EAAE,kBAAkB,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC;IAC1F;AAEU,IAAA,SAAS,CAAC,KAAa,EAAA;QAC/B,IAAI,CAAC,KAAK,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,QAAQ,KAAK,QAAQ,KAAK,KAAK,CAAC,EAAE,CAAC;IAC/E;IAEU,QAAQ,CAAC,KAAa,EAAE,KAAuB,EAAA;QACvD,IAAI,CAAC,KAAK,CAAC;AACT,YAAA,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,QAAQ,MAAM,QAAQ,KAAK,KAAK,GAAG,EAAE,GAAG,GAAG,EAAE,GAAG,KAAK,EAAE,GAAG,GAAG,CAAC,CAAC;AAC5F,SAAA,CAAC;IACJ;IAEU,QAAQ,GAAA;QAChB,IAAI,CAAC,KAAK,CAAC;AACT,YAAA,MAAM,EAAE;gBACN,GAAG,IAAI,CAAC,MAAM,EAAE;AAChB,gBAAA,EAAE,UAAU,EAAE,IAAI,CAAC,IAAI,EAAE,CAAC,iBAAiB,IAAI,EAAE,EAAE,eAAe,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE;AACpF,aAAA;AACF,SAAA,CAAC;IACJ;AAEU,IAAA,WAAW,CAAC,KAAa,EAAA;QACjC,IAAI,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,QAAQ,KAAK,QAAQ,KAAK,KAAK,CAAC,EAAE,CAAC;IACnF;IAEU,UAAU,CAAC,KAAa,EAAE,KAA+B,EAAA;QACjE,IAAI,CAAC,KAAK,CAAC;AACT,YAAA,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,QAAQ,KACxC,QAAQ,KAAK,KAAK,GAAG,EAAE,GAAG,KAAK,EAAE,GAAG,KAAK,EAAE,GAAG,KAAK,CACpD;AACF,SAAA,CAAC;IACJ;wGAhEW,yBAAyB,EAAA,IAAA,EAAA,IAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA;AAAzB,IAAA,OAAA,IAAA,GAAA,EAAA,CAAA,oBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,SAAA,EAAA,IAAA,EAAA,yBAAyB,EAAA,YAAA,EAAA,IAAA,EAAA,QAAA,EAAA,oBAAA,EAAA,eAAA,EAAA,IAAA,EAAA,QAAA,EAAA,EAAA,EAAA,QAAA,EA/M1B,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA6MT,EAAA,CAAA,EAAA,QAAA,EAAA,IAAA,EAAA,YAAA,EAAA,CAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EA/MS,uBAAuB,EAAA,QAAA,EAAA,kBAAA,EAAA,MAAA,EAAA,CAAA,UAAA,CAAA,EAAA,CAAA,EAAA,eAAA,EAAA,EAAA,CAAA,uBAAA,CAAA,MAAA,EAAA,CAAA;;4FAiNtB,yBAAyB,EAAA,UAAA,EAAA,CAAA;kBApNrC,SAAS;AAAC,YAAA,IAAA,EAAA,CAAA;AACT,oBAAA,QAAQ,EAAE,oBAAoB;AAC9B,oBAAA,UAAU,EAAE,IAAI;oBAChB,OAAO,EAAE,CAAC,uBAAuB,CAAC;oBAClC,eAAe,EAAE,uBAAuB,CAAC,MAAM;AAC/C,oBAAA,QAAQ,EAAE,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA6MT,EAAA,CAAA;AACF,iBAAA;;;ACjPD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA0DG;MA6IU,4BAA4B,CAAA;AAC9B,IAAA,KAAK,GAAG,KAAK,CAAC,aAAa,iDAAC;AAC5B,IAAA,KAAK,GAAG,KAAK,CAAqB,SAAS,iDAAC;AAC5C,IAAA,WAAW,GAAG,KAAK,CAAC,EAAE,uDAAC;AAChC;;;AAGG;AACM,IAAA,QAAQ,GAAG,KAAK,CAAqB,SAAS,oDAAC;AACxD;;;AAGG;AACM,IAAA,OAAO,GAAG,KAAK,CAAoB,EAAE,mDAAC;AAC/C;;;;AAIG;AACM,IAAA,MAAM,GAAG,KAAK,CAAwB,EAAE,kDAAC;AAClD;;;;;;;;AAQG;IACM,OAAO,GAAG,KAAK,CAA2B,EAAE,IAAI,EAAE,SAAS,EAAE,EAAA,IAAA,SAAA,GAAA,CAAA,EAAA,SAAA,EAAA,SAAA,EAAA,CAAA,GAAA,EAAA,CAAA,CAAC;AACvE;;;AAGG;AACM,IAAA,YAAY,GAAG,KAAK,CAAgC,SAAS,wDAAC;AACvE;;;;;;;;AAQG;IACM,WAAW,GAAG,KAAK,CAA2B,EAAE,WAAW,EAAE,IAAI,EAAE,EAAA,IAAA,SAAA,GAAA,CAAA,EAAA,SAAA,EAAA,aAAA,EAAA,CAAA,GAAA,EAAA,CAAA,CAAC;AAC7E;;;;;;;;;;;;;;;;;;AAkBG;AACM,IAAA,cAAc,GAAG,KAAK,CAAC,KAAK,0DAAC;AAC7B,IAAA,gBAAgB,GAAG,KAAK,CAAC,KAAK,4DAAC;AAC/B,IAAA,SAAS,GAAG,KAAK,CAAC,gBAAgB,qDAAC;IAEnC,WAAW,GAAG,MAAM,EAAU;IAE9B,QAAQ,GAAG,QAAQ,CAAC,MAAM,IAAI,CAAC,MAAM,EAAE,CAAC,IAAI,CAAC,CAAC,KAAK,KAAK,KAAK,CAAC,QAAQ,KAAK,OAAO,CAAC,EAAA,IAAA,SAAA,GAAA,CAAA,EAAA,SAAA,EAAA,UAAA,EAAA,CAAA,GAAA,EAAA,CAAA,CAAC;;AAGpF,IAAA,UAAU,GAAG,QAAQ,CAAC,MAAK;AAClC,QAAA,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,EAAE;AAC9B,QAAA,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC;AAC7C,QAAA,OAAO,OAAO,CAAC,MAAM,GAAG,EAAE,GAAG,CAAA,EAAG,KAAK,CAAA,GAAA,CAAK,GAAG,KAAK;AACpD,IAAA,CAAC,sDAAC;AAEF;;;AAGG;AACM,IAAA,UAAU,GAAG,QAAQ,CAAC,MAAM,uBAAuB,CAAC,IAAI,CAAC,YAAY,EAAE,CAAC,sDAAC;AAElF;;;;;;;AAOG;AACM,IAAA,SAAS,GAAG,QAAQ,CAAC,MAC5B,IAAI,CAAC,cAAc,EAAE,GAAG,yCAAyC,GAAG,gBAAgB,qDACrF;AAED;;;;AAIG;IACM,SAAS,GAAG,QAAQ,CAAC,MAC5B,IAAI,CAAC,cAAc;AACjB,UAAE,mFAAmF;YACnF;UACA,iFAAiF,EAAA,IAAA,SAAA,GAAA,CAAA,EAAA,SAAA,EAAA,WAAA,EAAA,CAAA,GAAA,EAAA,CAAA,CACtF;;IAGQ,QAAQ,GAAG,QAAQ,CAAC,OAAO,IAAI,CAAC,cAAc,EAAE,GAAG,MAAM,GAAG,IAAI,CAAC,EAAA,IAAA,SAAA,GAAA,CAAA,EAAA,SAAA,EAAA,UAAA,EAAA,CAAA,GAAA,EAAA,CAAA,CAAC;;AAGlE,IAAA,SAAS,GAAG,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,WAAW,qDAAC;AAEpE;;;AAGG;IACM,aAAa,GAAG,QAAQ,CAAC,MAChC,IAAI,CAAC,WAAW,EAAE,CAAC,WAAW,GAAG,SAAS,GAAG,IAAI,CAAC,WAAW,EAAE,CAAC,MAAM,EAAA,IAAA,SAAA,GAAA,CAAA,EAAA,SAAA,EAAA,eAAA,EAAA,CAAA,GAAA,EAAA,CAAA,CACvE;AAED;;;;AAIG;IACM,UAAU,GAAG,QAAQ,CAAC,OAAO,IAAI,CAAC,SAAS,EAAE,GAAG,0BAA0B,GAAG,EAAE,CAAC,EAAA,IAAA,SAAA,GAAA,CAAA,EAAA,SAAA,EAAA,YAAA,EAAA,CAAA,GAAA,EAAA,CAAA,CAAC;;AAGjF,IAAA,OAAO,GAAG,QAAQ,CAAC,MAAK;AAC/B,QAAA,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,EAAE;AAC9B,QAAA,OAAO,OAAO,CAAC,IAAI,KAAK,QAAQ,GAAG,OAAO,GAAG,SAAS;AACxD,IAAA,CAAC,mDAAC;AAEF;;;;AAIG;IACM,YAAY,GAAG,QAAQ,CAAC,MAC/B,IAAI,CAAC,OAAO,EAAE,EAAE,QAAQ,GAAG,2BAA2B,GAAG,4BAA4B,EAAA,IAAA,SAAA,GAAA,CAAA,EAAA,SAAA,EAAA,cAAA,EAAA,CAAA,GAAA,EAAA,CAAA,CACtF;AAED;;;;;;;AAOG;AACM,IAAA,WAAW,GAAG,QAAQ,CAAqB,MAAK;AACvD,QAAA,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,EAAE;AAC9B,QAAA,IAAI,OAAO,CAAC,IAAI,KAAK,SAAS,EAAE;AAC9B,YAAA,OAAO,SAAS;QAClB;AACA,QAAA,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO;AAC/B,QAAA,QACE,CAAA,EAAG,OAAO,CAAC,SAAS,CAAC,MAAM,CAAA,WAAA,EAAc,OAAO,CAAC,SAAS,CAAC,MAAM,CAAA,YAAA,CAAc;AAC/E,YAAA,CAAA,EAAG,OAAO,CAAC,OAAO,CAAC,MAAM,CAAA,sBAAA,EAAyB,qBAAqB,CAAC,OAAO,CAAC,CAAA,MAAA,CAAQ;AAE5F,IAAA,CAAC,uDAAC;AAEF;;;;AAIG;AACM,IAAA,WAAW,GAAG,QAAQ,CAAC,MAAK;AACnC,QAAA,MAAM,EAAE,GAAG,IAAI,CAAC,SAAS,EAAE;QAC3B,MAAM,KAAK,GAAa,EAAE;;;AAG1B,QAAA,IAAI,IAAI,CAAC,SAAS,EAAE,EAAE;AACpB,YAAA,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,CAAA,QAAA,CAAU,CAAC;QAC7B;QACA,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC,MAAM,GAAG,CAAC,EAAE;AAC5B,YAAA,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,CAAA,OAAA,CAAS,CAAC;QAC5B;AACA,QAAA,IAAI,IAAI,CAAC,OAAO,EAAE,EAAE;AAClB,YAAA,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,CAAA,QAAA,CAAU,CAAC;QAC7B;AACA,QAAA,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,CAAA,KAAA,CAAO,CAAC;AACxB,QAAA,OAAO,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC;AACxB,IAAA,CAAC,uDAAC;AAEQ,IAAA,UAAU,CAAC,KAAiB,EAAA;AACpC,QAAA,IAAI,KAAK,CAAC,QAAQ,KAAK,SAAS,EAAE;AAChC,YAAA,OAAO,4CAA4C;QACrD;AACA,QAAA,IAAI,KAAK,CAAC,QAAQ,KAAK,MAAM,EAAE;AAC7B,YAAA,OAAO,yCAAyC;QAClD;AACA,QAAA,OAAO,kBAAkB;IAC3B;AAEU,IAAA,IAAI,CAAC,KAAY,EAAA;AACzB,QAAA,OAAQ,KAAK,CAAC,MAA8B,CAAC,KAAK;IACpD;wGAxMW,4BAA4B,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA;AAA5B,IAAA,OAAA,IAAA,GAAA,EAAA,CAAA,oBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,SAAA,EAAA,IAAA,EAAA,4BAA4B,EAAA,YAAA,EAAA,IAAA,EAAA,QAAA,EAAA,uBAAA,EAAA,MAAA,EAAA,EAAA,KAAA,EAAA,EAAA,iBAAA,EAAA,OAAA,EAAA,UAAA,EAAA,OAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,KAAA,EAAA,EAAA,iBAAA,EAAA,OAAA,EAAA,UAAA,EAAA,OAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,WAAA,EAAA,EAAA,iBAAA,EAAA,aAAA,EAAA,UAAA,EAAA,aAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,QAAA,EAAA,EAAA,iBAAA,EAAA,UAAA,EAAA,UAAA,EAAA,UAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,OAAA,EAAA,EAAA,iBAAA,EAAA,SAAA,EAAA,UAAA,EAAA,SAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,MAAA,EAAA,EAAA,iBAAA,EAAA,QAAA,EAAA,UAAA,EAAA,QAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,OAAA,EAAA,EAAA,iBAAA,EAAA,SAAA,EAAA,UAAA,EAAA,SAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,YAAA,EAAA,EAAA,iBAAA,EAAA,cAAA,EAAA,UAAA,EAAA,cAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,WAAA,EAAA,EAAA,iBAAA,EAAA,aAAA,EAAA,UAAA,EAAA,aAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,cAAA,EAAA,EAAA,iBAAA,EAAA,gBAAA,EAAA,UAAA,EAAA,gBAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,gBAAA,EAAA,EAAA,iBAAA,EAAA,kBAAA,EAAA,UAAA,EAAA,kBAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,SAAA,EAAA,EAAA,iBAAA,EAAA,WAAA,EAAA,UAAA,EAAA,WAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,EAAA,OAAA,EAAA,EAAA,WAAA,EAAA,aAAA,EAAA,EAAA,QAAA,EAAA,EAAA,EAAA,QAAA,EA5H7B,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA0HT,EAAA,CAAA,EAAA,QAAA,EAAA,IAAA,EAAA,eAAA,EAAA,EAAA,CAAA,uBAAA,CAAA,MAAA,EAAA,CAAA;;4FAEU,4BAA4B,EAAA,UAAA,EAAA,CAAA;kBAhIxC,SAAS;AAAC,YAAA,IAAA,EAAA,CAAA;AACT,oBAAA,QAAQ,EAAE,uBAAuB;AACjC,oBAAA,UAAU,EAAE,IAAI;oBAChB,eAAe,EAAE,uBAAuB,CAAC,MAAM;AAC/C,oBAAA,QAAQ,EAAE,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA0HT,EAAA,CAAA;AACF,iBAAA;;;ACtMD;;;;;;;;;;;;;;;;;;;;;;;;;;;AA2BG;AAuBH,MAAM,KAAK,GAA6B,CAAC,YAAY,EAAE,OAAO,EAAE,iBAAiB,CAAC;AA+K5E,MAAO,8BAA+B,SAAQ,oBAAmC,CAAA;IACnE,UAAU,GAA0B,YAAY;IAE/C,KAAK,GAAG,KAAK;AAEf,IAAA,UAAU,GAAG,MAAM,CAAC,oBAAoB,CAAC;AACzC,IAAA,QAAQ,GAAG,MAAM,CAAC,sBAAsB,CAAC;AAE1D;;;;AAIG;AACM,IAAA,QAAQ,GAAG,QAAQ,CAAC,MAAM,IAAI,CAAC,UAAU,CAAC,kBAAkB,EAAE,oDAAC;AAExE;;;AAGG;AACM,IAAA,cAAc,GAAG,QAAQ,CAAC,MAAM,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC,0DAAC;AAElF;;;;;AAKG;AAEH;;;;AAIG;IACM,WAAW,GAAG,QAAQ,CAAC,MAC9B,wBAAwB,CAAC,IAAI,CAAC,YAAY,EAAE,EAAE,IAAI,CAAC,UAAU,CAAC,cAAc,EAAE,CAAC,EAAA,IAAA,SAAA,GAAA,CAAA,EAAA,SAAA,EAAA,aAAA,EAAA,CAAA,GAAA,EAAA,CAAA,CAChF;AAED;;;;;;;;AAQG;AACM,IAAA,cAAc,GAAG,QAAQ,CAAC,MAAM,IAAI,CAAC,UAAU,CAAC,YAAY,EAAE,0DAAC;AAE/D,IAAA,gBAAgB,GAAG,QAAQ,CAAC,MAAM,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC,4DAAC;AAE3E,IAAA,gBAAgB,GAAG,QAAQ,CAClC,MAAM,IAAI,CAAC,IAAI,EAAE,CAAC,gBAAgB,IAAI,EAAE,4DACzC;AAEQ,IAAA,OAAO,GAAG,QAAQ,CAA0B,MAAM,IAAI,CAAC,IAAI,EAAE,CAAC,OAAO,IAAI,EAAE,mDAAC;IAE5E,WAAW,GAAG,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,aAAa,IAAI,EAAE,EAAE,IAAI,CAAC,IAAI,CAAC,EAAA,IAAA,SAAA,GAAA,CAAA,EAAA,SAAA,EAAA,aAAA,EAAA,CAAA,GAAA,EAAA,CAAA,CAAC;IAC1E,aAAa,GAAG,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,WAAW,IAAI,EAAE,EAAE,IAAI,CAAC,IAAI,CAAC,EAAA,IAAA,SAAA,GAAA,CAAA,EAAA,SAAA,EAAA,eAAA,EAAA,CAAA,GAAA,EAAA,CAAA,CAAC;AAEnF;;;;;;;;;AASG;IACM,KAAK,GAAG,kBAAkB,CAAC,IAAI,CAAC,IAAI,EAAE,wBAAwB,CAAC;IAE/D,WAAW,GAAG,QAAQ,CAAC,MAAM,IAAI,CAAC,YAAY,EAAE,CAAC,GAAG,CAAC,CAAC,MAAM,KAAK,MAAM,CAAC,IAAI,CAAC,EAAA,IAAA,SAAA,GAAA,CAAA,EAAA,SAAA,EAAA,aAAA,EAAA,CAAA,GAAA,EAAA,CAAA,CAAC;AAEvF,IAAA,WAAA,GAAA;AACE,QAAA,KAAK,EAAE;AACP;;;;;;;;;;;;;AAaG;QACH,MAAM,CAAC,MAAK;AACV,YAAA,IAAI,IAAI,CAAC,KAAK,CAAC,gBAAgB,EAAE,IAAI,IAAI,CAAC,KAAK,CAAC,gBAAgB,EAAE,EAAE;gBAClE,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC;YAC3C;AACF,QAAA,CAAC,CAAC;IACJ;AAES,IAAA,UAAU,GAAG,QAAQ,CAAC,MAAK;AAClC,QAAA,MAAM,KAAK,GAAG,IAAI,CAAC,WAAW,EAAE;AAChC,QAAA,OAAO,KAAK,CAAC,MAAM,GAAG,EAAE,GAAG,CAAA,EAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA,GAAA,CAAK,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,GAAG;AAC5F,IAAA,CAAC,sDAAC;AAEF;;;;;;;;;;;;;AAaG;AACO,IAAA,MAAM,CAAC,KAAa,EAAA;QAC5B,IAAI,CAAC,WAAW,CAAC,EAAE,IAAI,EAAE,KAAsB,EAAE,EAAE,wBAAwB,CAAC;IAC9E;IAEU,aAAa,GAAA;QACrB,IAAI,CAAC,KAAK,CAAC;AACT,YAAA,gBAAgB,EAAE;gBAChB,GAAG,IAAI,CAAC,gBAAgB,EAAE;AAC1B,gBAAA,EAAE,KAAK,EAAE,EAAE,EAAE,UAAU,EAAE,EAAE,EAAE,YAAY,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE;AAChE,aAAA;AACF,SAAA,CAAC;IACJ;AAEU,IAAA,gBAAgB,CAAC,KAAa,EAAA;QACtC,IAAI,CAAC,KAAK,CAAC;AACT,YAAA,gBAAgB,EAAE,IAAI,CAAC,gBAAgB,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,QAAQ,KAAK,QAAQ,KAAK,KAAK,CAAC;AACtF,SAAA,CAAC;IACJ;IAEU,eAAe,CAAC,KAAa,EAAE,KAA+B,EAAA;QACtE,IAAI,CAAC,KAAK,CAAC;AACT,YAAA,gBAAgB,EAAE,IAAI,CAAC,gBAAgB,EAAE,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,QAAQ,KAC5D,QAAQ,KAAK,KAAK,GAAG,EAAE,GAAG,KAAK,EAAE,GAAG,KAAK,EAAE,GAAG,KAAK,CACpD;AACF,SAAA,CAAC;IACJ;AAEU,IAAA,SAAS,CAAC,GAAW,EAAA;AAC7B,QAAA,IAAI,CAAC,KAAK,CAAC,EAAE,aAAa,EAAE,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC;IAChD;AAEU,IAAA,WAAW,CAAC,GAAW,EAAA;AAC/B,QAAA,IAAI,CAAC,KAAK,CAAC,EAAE,WAAW,EAAE,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC;IAC9C;IAEU,QAAQ,GAAA;QAChB,IAAI,CAAC,KAAK,CAAC,EAAE,OAAO,EAAE,CAAC,GAAG,IAAI,CAAC,OAAO,EAAE,EAAE,EAAE,UAAU,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC;IAClE;AAEU,IAAA,WAAW,CAAC,KAAa,EAAA;QACjC,IAAI,CAAC,KAAK,CAAC,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,QAAQ,KAAK,QAAQ,KAAK,KAAK,CAAC,EAAE,CAAC;IACrF;IAEU,UAAU,CAAC,KAAa,EAAE,KAA4B,EAAA;QAC9D,IAAI,CAAC,KAAK,CAAC;AACT,YAAA,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,QAAQ,KAC1C,QAAQ,KAAK,KAAK,GAAG,EAAE,GAAG,KAAK,EAAE,GAAG,KAAK,EAAE,GAAG,KAAK,CACpD;AACF,SAAA,CAAC;IACJ;AAEQ,IAAA,KAAK,CAAC,GAAW,EAAA;QACvB,MAAM,MAAM,GAAG;aACZ,KAAK,CAAC,GAAG;aACT,GAAG,CAAC,CAAC,KAAK,KAAK,KAAK,CAAC,IAAI,EAAE;AAC3B,aAAA,MAAM,CAAC,CAAC,KAAK,KAAK,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC;AACtC,QAAA,OAAO,MAAM,CAAC,MAAM,GAAG,CAAC,GAAG,MAAM,GAAG,SAAS;IAC/C;wGA5KW,8BAA8B,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA;AAA9B,IAAA,OAAA,IAAA,GAAA,EAAA,CAAA,oBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,SAAA,EAAA,IAAA,EAAA,8BAA8B,EAAA,YAAA,EAAA,IAAA,EAAA,QAAA,EAAA,yBAAA,EAAA,eAAA,EAAA,IAAA,EAAA,QAAA,EAAA,EAAA,EAAA,QAAA,EAxK/B,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAsKT,EAAA,CAAA,EAAA,QAAA,EAAA,IAAA,EAAA,YAAA,EAAA,CAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EAxKS,4BAA4B,EAAA,QAAA,EAAA,wBAAA,EAAA,MAAA,EAAA,CAAA,OAAA,EAAA,WAAA,CAAA,EAAA,OAAA,EAAA,CAAA,aAAA,CAAA,EAAA,EAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EAAE,4BAA4B,EAAA,QAAA,EAAA,uBAAA,EAAA,MAAA,EAAA,CAAA,OAAA,EAAA,OAAA,EAAA,aAAA,EAAA,UAAA,EAAA,SAAA,EAAA,QAAA,EAAA,SAAA,EAAA,cAAA,EAAA,aAAA,EAAA,gBAAA,EAAA,kBAAA,EAAA,WAAA,CAAA,EAAA,OAAA,EAAA,CAAA,aAAA,CAAA,EAAA,EAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EAAE,uBAAuB,EAAA,QAAA,EAAA,kBAAA,EAAA,MAAA,EAAA,CAAA,UAAA,CAAA,EAAA,CAAA,EAAA,eAAA,EAAA,EAAA,CAAA,uBAAA,CAAA,MAAA,EAAA,CAAA;;4FA0KlF,8BAA8B,EAAA,UAAA,EAAA,CAAA;kBA7K1C,SAAS;AAAC,YAAA,IAAA,EAAA,CAAA;AACT,oBAAA,QAAQ,EAAE,yBAAyB;AACnC,oBAAA,UAAU,EAAE,IAAI;AAChB,oBAAA,OAAO,EAAE,CAAC,4BAA4B,EAAE,4BAA4B,EAAE,uBAAuB,CAAC;oBAC9F,eAAe,EAAE,uBAAuB,CAAC,MAAM;AAC/C,oBAAA,QAAQ,EAAE,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAsKT,EAAA,CAAA;AACF,iBAAA;;;AChOD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAoCG;AAyBH,MAAM,OAAO,GAAmC,CAAC,UAAU,EAAE,MAAM,EAAE,KAAK,CAAC;AAC3E,MAAM,UAAU,GAAkC,CAAC,QAAQ,EAAE,QAAQ,EAAE,QAAQ,EAAE,QAAQ,CAAC;AAC1F,MAAM,KAAK,GAAkC,CAAC,OAAO,EAAE,YAAY,EAAE,cAAc,CAAC;AACpF,MAAM,iBAAiB,GAAwC,CAAC,WAAW,EAAE,QAAQ,CAAC;AACtF,MAAM,UAAU,GAA4B,CAAC,OAAO,EAAE,WAAW,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,WAAW,CAAC;AAsZjG,MAAO,8BAA+B,SAAQ,oBAAmC,CAAA;IACnE,UAAU,GAA0B,YAAY;IAE/C,OAAO,GAAG,OAAO;IACjB,UAAU,GAAG,UAAU;IACvB,KAAK,GAAG,KAAK;IACb,UAAU,GAAG,UAAU;IACvB,gBAAgB,GAAG,iBAAiB;AAEtC,IAAA,UAAU,GAAG,MAAM,CAAC,oBAAoB,CAAC;AACzC,IAAA,QAAQ,GAAG,MAAM,CAAC,sBAAsB,CAAC;;AAGjD,IAAA,QAAQ,GAAG,QAAQ,CAAC,MAAM,IAAI,CAAC,UAAU,CAAC,kBAAkB,EAAE,oDAAC;AAExE;;;AAGG;AACM,IAAA,cAAc,GAAG,QAAQ,CAAC,MAAM,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC,0DAAC;AAElF;;;AAGG;AAEH;;;;AAIG;IACM,WAAW,GAAG,QAAQ,CAAC,MAC9B,wBAAwB,CAAC,IAAI,CAAC,YAAY,EAAE,EAAE,IAAI,CAAC,UAAU,CAAC,cAAc,EAAE,CAAC,EAAA,IAAA,SAAA,GAAA,CAAA,EAAA,SAAA,EAAA,aAAA,EAAA,CAAA,GAAA,EAAA,CAAA,CAChF;AAED;;;;;;AAMG;AACM,IAAA,cAAc,GAAG,QAAQ,CAAC,MAAM,IAAI,CAAC,UAAU,CAAC,YAAY,EAAE,0DAAC;AAE/D,IAAA,gBAAgB,GAAG,QAAQ,CAAC,MAAM,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC,4DAAC;AAE3E,IAAA,UAAU,GAAG,QAAQ,CAAC,MAAM,IAAI,CAAC,IAAI,EAAE,CAAC,MAAM,KAAK,UAAU,sDAAC;AAEvE,IAAA,WAAA,GAAA;AACE,QAAA,KAAK,EAAE;AACP;;;;;;;AAOG;QACH,MAAM,CAAC,MAAK;AACV,YAAA,IAAI,IAAI,CAAC,KAAK,CAAC,gBAAgB,EAAE,EAAE;gBACjC,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC;YAC3C;AACF,QAAA,CAAC,CAAC;IACJ;AAEA;;;;;;;;;;;;;;AAcG;IACM,KAAK,GAAG,kBAAkB,CAAC,IAAI,CAAC,IAAI,EAAE,wBAAwB,CAAC;AAExE;;;;;;;;AAQG;AACM,IAAA,IAAI,GAAG,QAAQ,CAAC,MACvB,IAAI,CAAC,KAAK,CAAC,iBAAiB,EAAE,CAAC,IAAI,CAAC,CAAC,KAAK,KAAK,KAAK,CAAC,aAAa,KAAK,IAAI,CAAC,QAAQ,EAAE,CAAC,gDACxF;AAEQ,IAAA,MAAM,GAAG,QAAQ,CAAmC,MAAM,IAAI,CAAC,IAAI,EAAE,CAAC,MAAM,IAAI,EAAE,kDAAC;IAEnF,WAAW,GAAG,QAAQ,CAAC,MAAM,IAAI,CAAC,YAAY,EAAE,CAAC,GAAG,CAAC,CAAC,MAAM,KAAK,MAAM,CAAC,IAAI,CAAC,EAAA,IAAA,SAAA,GAAA,CAAA,EAAA,SAAA,EAAA,aAAA,EAAA,CAAA,GAAA,EAAA,CAAA,CAAC;IAE9E,eAAe,GAAG,QAAQ,CAAC,MAClC,IAAI,CAAC,UAAU,EAAE,CAAC,MAAM,CAAC,CAAC,SAAS,KAAK,SAAS,CAAC,IAAI,KAAK,OAAO,CAAC,EAAA,IAAA,SAAA,GAAA,CAAA,EAAA,SAAA,EAAA,iBAAA,EAAA,CAAA,GAAA,EAAA,CAAA,CACpE;AAES,IAAA,MAAM,CAAC,KAAa,EAAA;AAC5B,QAAA,OAAO,KAA2B;IACpC;AAEU,IAAA,WAAW,CAAC,KAAa,EAAA;AACjC,QAAA,OAAO,KAAqB;IAC9B;AAEA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAiCG;AACO,IAAA,QAAQ,CAAC,KAAa,EAAA;QAC9B,MAAM,MAAM,GAAG,KAA4B;AAC3C,QAAA,MAAM,MAAM,GACV,MAAM,KAAK,UAAU,GAAG,EAAE,MAAM,EAAE,SAAS,EAAE,IAAI,CAAC,IAAI,EAAE,CAAC,SAAS,IAAI,QAAQ,EAAE,GAAG,EAAE,MAAM,EAAE;AAC/F,QAAA,IAAI,CAAC,WAAW,CAAC,MAAM,EAAE,wBAAwB,CAAC;IACpD;;AAGU,IAAA,SAAS,CAAC,QAAiB,EAAA;QACnC,IAAI,CAAC,KAAK,CAAC,EAAE,WAAW,EAAE,EAAE,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC,WAAW,IAAI,EAAE,CAAC,EAAE,QAAQ,EAAE,EAAE,CAAC;IAC/E;AAEA;;;;AAIG;IACO,iBAAiB,GAAA;QACzB,IAAI,CAAC,KAAK,CAAC,EAAE,WAAW,EAAE,SAAS,EAAE,CAAC;IACxC;IAEU,gBAAgB,GAAA;QACxB,IAAI,CAAC,KAAK,CAAC,EAAE,UAAU,EAAE,SAAS,EAAE,CAAC;IACvC;AAEU,IAAA,QAAQ,CAAC,KAAuE,EAAA;QACxF,IAAI,CAAC,KAAK,CAAC,EAAE,UAAU,EAAE,EAAE,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC,UAAU,IAAI,EAAE,CAAC,EAAE,GAAG,KAAK,EAAE,EAAE,CAAC;IAC7E;IAEU,QAAQ,GAAA;QAChB,IAAI,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,EAAE,EAAE,eAAe,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC;IACrE;AAEU,IAAA,WAAW,CAAC,KAAa,EAAA;QACjC,IAAI,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,QAAQ,KAAK,QAAQ,KAAK,KAAK,CAAC,EAAE,CAAC;IACnF;IAEU,UAAU,CAAC,KAAa,EAAE,KAAqC,EAAA;QACvE,IAAI,CAAC,KAAK,CAAC;AACT,YAAA,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,QAAQ,KACxC,QAAQ,KAAK,KAAK,GAAG,EAAE,GAAG,KAAK,EAAE,GAAG,KAAK,EAAE,GAAG,KAAK,CACpD;AACF,SAAA,CAAC;IACJ;IAEU,kBAAkB,CAAC,KAAa,EAAE,IAAY,EAAA;AACtD,QAAA,IAAI,CAAC,UAAU,CAAC,KAAK,EAAE;YACrB,oBAAoB,EAAE,IAAI,KAAK,EAAE,GAAG,SAAS,GAAG,IAAI;AACpD,YAAA,eAAe,EAAE,IAAI,KAAK,EAAE,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,KAAK,CAAC,EAAE,eAAe,GAAG,SAAS;AACjF,SAAA,CAAC;IACJ;wGAnMW,8BAA8B,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA;AAA9B,IAAA,OAAA,IAAA,GAAA,EAAA,CAAA,oBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,SAAA,EAAA,IAAA,EAAA,8BAA8B,EAAA,YAAA,EAAA,IAAA,EAAA,QAAA,EAAA,yBAAA,EAAA,eAAA,EAAA,IAAA,EAAA,QAAA,EAAA,EAAA,EAAA,QAAA,EA/Y/B,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6YT,EAAA,QAAA,EAAA,IAAA,EAAA,YAAA,EAAA,CAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EA/YS,4BAA4B,qQAAE,uBAAuB,EAAA,QAAA,EAAA,kBAAA,EAAA,MAAA,EAAA,CAAA,UAAA,CAAA,EAAA,CAAA,EAAA,eAAA,EAAA,EAAA,CAAA,uBAAA,CAAA,MAAA,EAAA,CAAA;;4FAiZpD,8BAA8B,EAAA,UAAA,EAAA,CAAA;kBApZ1C,SAAS;AAAC,YAAA,IAAA,EAAA,CAAA;AACT,oBAAA,QAAQ,EAAE,yBAAyB;AACnC,oBAAA,UAAU,EAAE,IAAI;AAChB,oBAAA,OAAO,EAAE,CAAC,4BAA4B,EAAE,uBAAuB,CAAC;oBAChE,eAAe,EAAE,uBAAuB,CAAC,MAAM;AAC/C,oBAAA,QAAQ,EAAE,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA6YT,EAAA,CAAA;AACF,iBAAA;;;ACtdD;;;;;;;;;;;;;;AAcG;MAqJU,yBAAyB,CAAA;AACnB,IAAA,KAAK,GAAG,MAAM,CAAC,gBAAgB,CAAC;AAExC,IAAA,OAAO,GAAG,QAAQ,CAAC,MAAK;QAC/B,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,gBAAgB,EAAE;QAC1C,OAAO,IAAI,KAAK,SAAS,GAAG,SAAS,GAAG,WAAW,CAAC,IAAI,CAAC,KAAK,CAAC,UAAU,EAAE,EAAE,IAAI,CAAC;AACpF,IAAA,CAAC,mDAAC;AAEO,IAAA,SAAS,GAAG,MAAM,CAAC,EAAE,qDAAC;AAEtB,IAAA,cAAc,GAAG,QAAQ,CAAC,MAAK;AACtC,QAAA,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,EAAE;QAC5B,OAAO,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,eAAe,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC;AAChE,IAAA,CAAC,0DAAC;AAEO,IAAA,SAAS,GAAG,QAAQ,CAAC,MAAK;AACjC,QAAA,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,EAAE;QAC5B,MAAM,KAAK,GAAG,IAAI,CAAC,SAAS,EAAE,CAAC,IAAI,EAAE;AACrC,QAAA,OAAO,CAAC,CAAC,KAAK,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,IAAI,KAAK,KAAK,KAAK,CAAC,IAAI,CAAC,IAAI;AACjE,IAAA,CAAC,qDAAC;AAEF,IAAA,WAAA,GAAA;;;QAGE,MAAM,CAAC,MAAK;AACV,YAAA,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,EAAE;AAC5B,YAAA,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,KAAK,EAAE,IAAI,CAAC,IAAI,IAAI,EAAE,CAAC;AAC5C,QAAA,CAAC,CAAC;IACJ;AAEU,IAAA,MAAM,CAAC,WAAmB,EAAA;AAClC,QAAA,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,WAAW,EAAE,IAAI,CAAC,SAAS,EAAE,CAAC,IAAI,EAAE,CAAC;IAC7D;IAEU,SAAS,CAAC,KAA8B,EAAE,KAAa,EAAA;AAC/D,QAAA,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,EAAE;QAC5B,IAAI,CAAC,KAAK,EAAE;YACV;QACF;AACA,QAAA,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,KAAK,CAAC,UAAU,EAAE,KAAK,CAAC,IAAI,CAAC,IAAI,EAAE;AACvD,YAAA,CAAC,KAAK,GAAG,KAAK,KAAK,EAAE,GAAG,SAAS,GAAG,KAAK;AACjC,SAAA,CAAC;IACb;AAEU,IAAA,OAAO,CAAC,KAAY,EAAA;AAC5B,QAAA,OAAQ,KAAK,CAAC,MAA2B,CAAC,KAAK;IACjD;;;;AAKU,IAAA,YAAY,CAAC,IAAa,EAAA;AAClC,QAAA,OAAO,IAAsB;IAC/B;AAEU,IAAA,QAAQ,CAAC,IAAa,EAAA;AAC9B,QAAA,OAAO,IAAkB;IAC3B;AAEU,IAAA,MAAM,CAAC,IAAa,EAAA;AAC5B,QAAA,OAAO,IAAgB;IACzB;AAEU,IAAA,WAAW,CAAC,IAAa,EAAA;AACjC,QAAA,OAAO,IAAqB;IAC9B;AAEU,IAAA,WAAW,CAAC,IAAa,EAAA;AACjC,QAAA,OAAO,IAAqB;IAC9B;AAEU,IAAA,QAAQ,CAAC,IAAa,EAAA;AAC9B,QAAA,OAAO,IAAkB;IAC3B;AAEU,IAAA,WAAW,CAAC,IAAa,EAAA;AACjC,QAAA,OAAO,IAAyB;IAClC;AAEU,IAAA,UAAU,CAAC,IAAa,EAAA;AAChC,QAAA,OAAO,IAAoB;IAC7B;AAEU,IAAA,QAAQ,CAAC,IAAa,EAAA;AAC9B,QAAA,OAAO,IAAkB;IAC3B;AAEU,IAAA,WAAW,CAAC,IAAa,EAAA;AACjC,QAAA,OAAO,IAAqB;IAC9B;AAEU,IAAA,QAAQ,CAAC,IAAa,EAAA;AAC9B,QAAA,OAAO,IAA2B;IACpC;wGA7FW,yBAAyB,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA;AAAzB,IAAA,OAAA,IAAA,GAAA,EAAA,CAAA,oBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,SAAA,EAAA,IAAA,EAAA,yBAAyB,EAAA,YAAA,EAAA,IAAA,EAAA,QAAA,EAAA,oBAAA,EAAA,QAAA,EAAA,EAAA,EAAA,QAAA,EA9F1B,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4FT,EAAA,QAAA,EAAA,IAAA,EAAA,YAAA,EAAA,CAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EAzGC,8BAA8B,oEAC9B,2BAA2B,EAAA,QAAA,EAAA,sBAAA,EAAA,EAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EAC3B,oCAAoC,EAAA,QAAA,EAAA,gCAAA,EAAA,EAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EACpC,+BAA+B,sEAC/B,+BAA+B,EAAA,QAAA,EAAA,2BAAA,EAAA,EAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EAC/B,2BAA2B,EAAA,QAAA,EAAA,sBAAA,EAAA,EAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EAC3B,6BAA6B,mEAC7B,kCAAkC,EAAA,QAAA,EAAA,8BAAA,EAAA,EAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EAClC,yBAAyB,EAAA,QAAA,EAAA,oBAAA,EAAA,EAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EACzB,8BAA8B,oEAC9B,8BAA8B,EAAA,QAAA,EAAA,yBAAA,EAAA,CAAA,EAAA,eAAA,EAAA,EAAA,CAAA,uBAAA,CAAA,MAAA,EAAA,CAAA;;4FAiGrB,yBAAyB,EAAA,UAAA,EAAA,CAAA;kBA/GrC,SAAS;AAAC,YAAA,IAAA,EAAA,CAAA;AACT,oBAAA,QAAQ,EAAE,oBAAoB;AAC9B,oBAAA,UAAU,EAAE,IAAI;AAChB,oBAAA,OAAO,EAAE;wBACP,8BAA8B;wBAC9B,2BAA2B;wBAC3B,oCAAoC;wBACpC,+BAA+B;wBAC/B,+BAA+B;wBAC/B,2BAA2B;wBAC3B,6BAA6B;wBAC7B,kCAAkC;wBAClC,yBAAyB;wBACzB,8BAA8B;wBAC9B,8BAA8B;AAC/B,qBAAA;oBACD,eAAe,EAAE,uBAAuB,CAAC,MAAM;AAC/C,oBAAA,QAAQ,EAAE,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA4FT,EAAA,CAAA;AACF,iBAAA;;;AClKD;;;;;;;;;;;;;;;;;;;;;;;AAuBG;AAoBH;;;;;;AAMG;AACH,IAAI,WAAW,GAAG,CAAC;MA+FN,sBAAsB,CAAA;AACd,IAAA,KAAK,GAAG,MAAM,CAAC,gBAAgB,CAAC;AAElC,IAAA,SAAS,GAAG,SAAS,CAAC,QAAQ,CAAgC,KAAK,CAAC;IAElE,OAAO,GAAG,0BAA0B,WAAW,IAAI,CAAC,GAAG;AAE1E;;;AAGG;AACgB,IAAA,OAAO,GAAG,QAAQ,CAAC,MAAK;QACzC,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,gBAAgB,EAAE;QAC1C,OAAO,IAAI,KAAK,SAAS,GAAG,SAAS,GAAG,WAAW,CAAC,IAAI,CAAC,KAAK,CAAC,UAAU,EAAE,EAAE,IAAI,CAAC;AACpF,IAAA,CAAC,mDAAC;AAEiB,IAAA,aAAa,GAAG,QAAQ,CAAC,MAAK;AAC/C,QAAA,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,EAAE;AAC5B,QAAA,OAAO,KAAK,GAAG,eAAe,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,KAAK,GAAG,EAAE;AAC7D,IAAA,CAAC,yDAAC;AAEiB,IAAA,WAAW,GAAG,QAAQ,CAAC,MAAK;AAC7C,QAAA,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,EAAE;AAC5B,QAAA,OAAO,KAAK,GAAG,sBAAsB,CAAC,KAAK,CAAC,UAAU,CAAC,GAAG,EAAE;AAC9D,IAAA,CAAC,uDAAC;AAEF;;;AAGG;IACK,wBAAwB,GAAG,KAAK;AAExC,IAAA,WAAA,GAAA;QACE,MAAM,CAAC,MAAK;YACV,MAAM,EAAE,GAAG,IAAI,CAAC,SAAS,EAAE,CAAC,aAAa;AACzC,YAAA,MAAM,YAAY,GAAG,IAAI,CAAC,KAAK,CAAC,gBAAgB,EAAE,IAAI,IAAI,CAAC,OAAO,EAAE,KAAK,SAAS;;;;;AAKlF,YAAA,IAAI,YAAY,IAAI,CAAC,EAAE,CAAC,IAAI,EAAE;gBAC5B,EAAE,CAAC,SAAS,EAAE;YAChB;AAAO,iBAAA,IAAI,CAAC,YAAY,IAAI,EAAE,CAAC,IAAI,EAAE;gBACnC,EAAE,CAAC,KAAK,EAAE;YACZ;AACF,QAAA,CAAC,CAAC;IACJ;AAEA;;;;;;;;;;;;AAYG;IACO,QAAQ,GAAA;AAChB,QAAA,IAAI,CAAC,KAAK,CAAC,eAAe,EAAE;IAC9B;AAEA;;;;;;;;;;;AAWG;IACO,OAAO,GAAA;QACf,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC,aAAa,CAAC,IAAI,EAAE;YACvC;QACF;AACA,QAAA,IAAI,CAAC,KAAK,CAAC,eAAe,EAAE;IAC9B;AAEU,IAAA,aAAa,CAAC,KAAY,EAAA;AAClC,QAAA,IAAI,CAAC,wBAAwB,GAAG,KAAK,CAAC,MAAM,KAAK,IAAI,CAAC,SAAS,EAAE,CAAC,aAAa;IACjF;AAEA;;;;;;AAMG;AACO,IAAA,aAAa,CAAC,KAAY,EAAA;AAClC,QAAA,IAAI,IAAI,CAAC,wBAAwB,IAAI,KAAK,CAAC,MAAM,KAAK,IAAI,CAAC,SAAS,EAAE,CAAC,aAAa,EAAE;AACpF,YAAA,IAAI,CAAC,KAAK,CAAC,eAAe,EAAE;QAC9B;IACF;wGAnGW,sBAAsB,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA;AAAtB,IAAA,OAAA,IAAA,GAAA,EAAA,CAAA,oBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,SAAA,EAAA,IAAA,EAAA,sBAAsB,EAAA,YAAA,EAAA,IAAA,EAAA,QAAA,EAAA,iBAAA,EAAA,WAAA,EAAA,CAAA,EAAA,YAAA,EAAA,WAAA,EAAA,KAAA,EAAA,IAAA,EAAA,SAAA,EAAA,CAAA,KAAA,CAAA,EAAA,WAAA,EAAA,IAAA,EAAA,QAAA,EAAA,IAAA,EAAA,CAAA,EAAA,QAAA,EAAA,EAAA,EAAA,QAAA,EAxFvB,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAsFT,EAAA,CAAA,EAAA,QAAA,EAAA,IAAA,EAAA,YAAA,EAAA,CAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EAxFS,qBAAqB,EAAA,QAAA,EAAA,gBAAA,EAAA,MAAA,EAAA,CAAA,UAAA,CAAA,EAAA,EAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EAAE,yBAAyB,EAAA,QAAA,EAAA,oBAAA,EAAA,EAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EAAE,oBAAoB,EAAA,QAAA,EAAA,eAAA,EAAA,MAAA,EAAA,CAAA,YAAA,CAAA,EAAA,CAAA,EAAA,eAAA,EAAA,EAAA,CAAA,uBAAA,CAAA,MAAA,EAAA,CAAA;;4FA0FrE,sBAAsB,EAAA,UAAA,EAAA,CAAA;kBA7FlC,SAAS;AAAC,YAAA,IAAA,EAAA,CAAA;AACT,oBAAA,QAAQ,EAAE,iBAAiB;AAC3B,oBAAA,UAAU,EAAE,IAAI;AAChB,oBAAA,OAAO,EAAE,CAAC,qBAAqB,EAAE,yBAAyB,EAAE,oBAAoB,CAAC;oBACjF,eAAe,EAAE,uBAAuB,CAAC,MAAM;AAC/C,oBAAA,QAAQ,EAAE,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAsFT,EAAA,CAAA;AACF,iBAAA;iGAIgF,KAAK,EAAA,EAAA,QAAA,EAAA,IAAA,EAAA,CAAA,EAAA,CAAA,EAAA,EAAA,CAAA;;ACpJtF;;;;;;;;;;;;;;;;AAgBG;MAqEU,uBAAuB,CAAA;;IAEzB,aAAa,GAAG,MAAM,EAAyB;AAErC,IAAA,MAAM,GAAG,MAAM,CAAC,EAAE,kDAAC;AAE7B,IAAA,OAAO,GAAG,QAAQ,CAA6B,MAAK;AAC3D,QAAA,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE;AACjD,QAAA,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE;AACvB,YAAA,OAAO,mBAAmB;QAC5B;AACA,QAAA,OAAO,mBAAmB,CAAC,MAAM,CAC/B,CAAC,QAAQ,KACP,QAAQ,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,MAAM,CAAC;YAC7C,QAAQ,CAAC,WAAW,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,MAAM,CAAC,CACtD;AACH,IAAA,CAAC,mDAAC;AAEF;;;;AAIG;AACM,IAAA,MAAM,GAAG,QAAQ,CAAiC,MAAK;AAC9D,QAAA,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,EAAE;AAC9B,QAAA,MAAM,MAAM,GAA0B;AACpC,YAAA;AACE,gBAAA,GAAG,EAAE,SAAS;AACd,gBAAA,KAAK,EAAE,UAAU;AACjB,gBAAA,UAAU,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC,QAAQ,KAAK,QAAQ,CAAC,QAAQ,CAAC;AAC5D,aAAA;AACD,YAAA;AACE,gBAAA,GAAG,EAAE,YAAY;AACjB,gBAAA,KAAK,EAAE,gBAAgB;AACvB,gBAAA,UAAU,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC,QAAQ,KAAK,CAAC,QAAQ,CAAC,QAAQ,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC;AACjF,aAAA;AACD,YAAA;AACE,gBAAA,GAAG,EAAE,OAAO;AACZ,gBAAA,KAAK,EAAE,WAAW;AAClB,gBAAA,UAAU,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC,QAAQ,KAAK,QAAQ,CAAC,MAAM,CAAC;AAC1D,aAAA;SACF;AACD,QAAA,OAAO,MAAM,CAAC,MAAM,CAAC,CAAC,KAAK,KAAK,KAAK,CAAC,UAAU,CAAC,MAAM,GAAG,CAAC,CAAC;AAC9D,IAAA,CAAC,kDAAC;AAEQ,IAAA,WAAW,CAAC,QAAyB,EAAA;AAC7C,QAAA,OAAO,sBAAsB,CAAC,QAAQ,CAAC,UAAU,CAAC;IACpD;;AAGU,IAAA,OAAO,CAAC,QAAyB,EAAA;QACzC,OAAO,QAAQ,CAAC;AACd,cAAE,CAAA,EAAG,QAAQ,CAAC,WAAW,CAAA,gDAAA;AACzB,cAAE,QAAQ,CAAC,WAAW;IAC1B;AAEU,IAAA,IAAI,CAAC,KAAY,EAAA;AACzB,QAAA,OAAQ,KAAK,CAAC,MAA2B,CAAC,KAAK;IACjD;wGA1DW,uBAAuB,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA;AAAvB,IAAA,OAAA,IAAA,GAAA,EAAA,CAAA,oBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,SAAA,EAAA,IAAA,EAAA,uBAAuB,EAAA,YAAA,EAAA,IAAA,EAAA,QAAA,EAAA,kBAAA,EAAA,OAAA,EAAA,EAAA,aAAA,EAAA,eAAA,EAAA,EAAA,QAAA,EAAA,EAAA,EAAA,QAAA,EA/CxB,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6CT,EAAA,QAAA,EAAA,IAAA,EAAA,YAAA,EAAA,CAAA,EAAA,IAAA,EAAA,UAAA,EAAA,IAAA,EA/CS,WAAW,kQAAE,oBAAoB,EAAA,QAAA,EAAA,eAAA,EAAA,MAAA,EAAA,CAAA,YAAA,CAAA,EAAA,CAAA,EAAA,eAAA,EAAA,EAAA,CAAA,uBAAA,CAAA,MAAA,EAAA,CAAA;;4FAiDhC,uBAAuB,EAAA,UAAA,EAAA,CAAA;kBApDnC,SAAS;AAAC,YAAA,IAAA,EAAA,CAAA;AACT,oBAAA,QAAQ,EAAE,kBAAkB;AAC5B,oBAAA,UAAU,EAAE,IAAI;AAChB,oBAAA,OAAO,EAAE,CAAC,WAAW,EAAE,oBAAoB,CAAC;oBAC5C,eAAe,EAAE,uBAAuB,CAAC,MAAM;AAC/C,oBAAA,QAAQ,EAAE,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA6CT,EAAA,CAAA;AACF,iBAAA;;;ACpFD;;;;;;;;;;;;;;;AAeG;MAyGU,sBAAsB,CAAA;AAChB,IAAA,KAAK,GAAG,MAAM,CAAC,gBAAgB,CAAC;AAC9B,IAAA,UAAU,GAAG,MAAM,CAAC,oBAAoB,CAAC;AAEnD,IAAA,MAAM,GAAG,QAAQ,CAAC,MAAM,IAAI,CAAC,KAAK,CAAC,aAAa,EAAE,kDAAC;AAEnD,IAAA,QAAQ,GAAG,QAAQ,CAAkB,MAAK;AACjD,QAAA,MAAM,QAAQ,GAAG,IAAI,CAAC,MAAM,EAAE;QAC9B,OAAO;AACL,YAAA;AACE,gBAAA,KAAK,EAAE,uBAAuB;AAC9B,gBAAA,IAAI,EAAE,wEAAwE;AAC9E,gBAAA,UAAU,EAAE,QAAQ,EAAE,kBAAkB,IAAI,EAAE;AAC/C,aAAA;AACD,YAAA;AACE,gBAAA,KAAK,EAAE,wBAAwB;AAC/B,gBAAA,IAAI,EAAE,2EAA2E;AACjF,gBAAA,UAAU,EAAE,QAAQ,EAAE,cAAc,IAAI,EAAE;AAC3C,aAAA;SACF;AACH,IAAA,CAAC,oDAAC;IAEO,OAAO,GAAG,QAAQ,CAAC,MAC1B,IAAI,CAAC,QAAQ,EAAE,CAAC,KAAK,CAAC,CAAC,OAAO,KAAK,OAAO,CAAC,UAAU,CAAC,MAAM,KAAK,CAAC,CAAC,EAAA,IAAA,SAAA,GAAA,CAAA,EAAA,SAAA,EAAA,SAAA,EAAA,CAAA,GAAA,EAAA,CAAA,CACpE;;AAGS,IAAA,OAAO,CAAC,KAAc,EAAA;QAC9B,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,SAAS,EAAE;AACzC,YAAA,OAAO,MAAM;QACf;QACA,OAAO,OAAO,KAAK,KAAK,QAAQ,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,GAAG,MAAM,CAAC,KAAK,CAAC;IAC1E;wGAhCW,sBAAsB,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA;AAAtB,IAAA,OAAA,IAAA,GAAA,EAAA,CAAA,oBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,SAAA,EAAA,IAAA,EAAA,sBAAsB,EAAA,YAAA,EAAA,IAAA,EAAA,QAAA,EAAA,iBAAA,EAAA,QAAA,EAAA,EAAA,EAAA,QAAA,EA5EvB,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA0ET,EAAA,CAAA,EAAA,QAAA,EAAA,IAAA,EAAA,eAAA,EAAA,EAAA,CAAA,uBAAA,CAAA,MAAA,EAAA,CAAA;;4FAEU,sBAAsB,EAAA,UAAA,EAAA,CAAA;kBAhFlC,SAAS;AAAC,YAAA,IAAA,EAAA,CAAA;AACT,oBAAA,QAAQ,EAAE,iBAAiB;AAC3B,oBAAA,UAAU,EAAE,IAAI;oBAChB,eAAe,EAAE,uBAAuB,CAAC,MAAM;AAC/C,oBAAA,QAAQ,EAAE,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA0ET,EAAA,CAAA;AACF,iBAAA;;;ACvHD;;;;;;;;;;;AAWG;MA6FU,uBAAuB,CAAA;AACjB,IAAA,KAAK,GAAG,MAAM,CAAC,gBAAgB,CAAC;AAExC,IAAA,SAAS,GAAG,QAAQ,CAAC,MAAM,IAAI,CAAC,KAAK,CAAC,cAAc,EAAE,KAAK,SAAS,qDAAC;AAErE,IAAA,IAAI,GAAG,QAAQ,CAAiB,MAAK;QAC5C,MAAM,UAAU,GAAG,IAAI,CAAC,KAAK,CAAC,UAAU,EAAE;QAC1C,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,EAAE;QACzC,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,gBAAgB,EAAE;QAC9C,MAAM,cAAc,GAAG,IAAI,CAAC,KAAK,CAAC,cAAc,EAAE;AAElD,QAAA,MAAM,OAAO,GAAG,WAAW,CAAC,UAAU,CAAC;;;AAGvC,QAAA,MAAM,OAAO,GACX,cAAc,CAAC,MAAM,GAAG;cACpB,CAAC,GAAG,OAAO,CAAC,CAAC,IAAI,CACf,CAAC,IAAI,EAAE,KAAK,KACV,WAAW,CAAC,cAAc,EAAE,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC;gBAC3C,WAAW,CAAC,cAAc,EAAE,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC;cAEhD,OAAO;AAEb,QAAA,OAAO,OAAO,CAAC,GAAG,CAAC,CAAC,KAAK,KAAI;AAC3B,YAAA,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC;AAC3C,YAAA,MAAM,aAAa,GAAG,MAAM,EAAE,OAAO,IAAI,EAAE;YAC3C,MAAM,YAAY,GAAoB,EAAE;AACxC,YAAA,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU;YAC9B,KAAK,MAAM,SAAS,IAAI,uBAAuB,CAAC,KAAK,CAAC,EAAE;gBACtD,IAAI,CAAC,SAAS,CAAC,MAAM,IAAI,CAAC,SAAS,CAAC,UAAU,EAAE;oBAC9C;gBACF;AACA,gBAAA,KAAK,MAAM,MAAM,IAAI,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,UAAU,CAAC,EAAE,OAAO,IAAI,EAAE,EAAE;oBACrE,MAAM,GAAG,GAAG,MAAM,CAAC,IAAI,CAAC,WAAW,EAAE;oBACrC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE;AAClB,wBAAA,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC;AACb,wBAAA,YAAY,CAAC,IAAI,CAAC,MAAM,CAAC;oBAC3B;gBACF;YACF;YACA,MAAM,WAAW,GAAG,IAAI,GAAG,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC,MAAM,KAAK,MAAM,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,CAAC;YACrF,OAAO;AACL,gBAAA,QAAQ,EAAE,KAAK,CAAC,IAAI,CAAC,IAAI;gBACzB,aAAa,EAAE,eAAe,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,KAAK;AACtD,gBAAA,UAAU,EAAE,KAAK,CAAC,IAAI,CAAC,IAAI,KAAK,QAAQ;AACxC,gBAAA,YAAY,EAAE,MAAM,EAAE,YAAY,KAAK,KAAK;;;;;AAK5C,gBAAA,SAAS,EAAE,MAAM,EAAE,SAAS,KAAK,IAAI;gBACrC,YAAY;gBACZ,aAAa;AACb,gBAAA,UAAU,EAAE,IAAI,GAAG,CACjB;AACG,qBAAA,GAAG,CAAC,CAAC,MAAM,KAAK,MAAM,CAAC,IAAI,CAAC,WAAW,EAAE;AACzC,qBAAA,MAAM,CAAC,CAAC,IAAI,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CACrC;AACD,gBAAA,OAAO,EAAE;AACN,qBAAA,MAAM,CAAC,CAAC,MAAM,KAAK,CAAC,WAAW,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC;qBAC9D,GAAG,CAAC,CAAC,MAAM,KAAK,MAAM,CAAC,IAAI,CAAC;aAChC;AACH,QAAA,CAAC,CAAC;AACJ,IAAA,CAAC,gDAAC;wGA/DS,uBAAuB,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA;AAAvB,IAAA,OAAA,IAAA,GAAA,EAAA,CAAA,oBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,SAAA,EAAA,IAAA,EAAA,uBAAuB,EAAA,YAAA,EAAA,IAAA,EAAA,QAAA,EAAA,kBAAA,EAAA,QAAA,EAAA,EAAA,EAAA,QAAA,EAlExB,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAgET,EAAA,CAAA,EAAA,QAAA,EAAA,IAAA,EAAA,eAAA,EAAA,EAAA,CAAA,uBAAA,CAAA,MAAA,EAAA,CAAA;;4FAEU,uBAAuB,EAAA,UAAA,EAAA,CAAA;kBAtEnC,SAAS;AAAC,YAAA,IAAA,EAAA,CAAA;AACT,oBAAA,QAAQ,EAAE,kBAAkB;AAC5B,oBAAA,UAAU,EAAE,IAAI;oBAChB,eAAe,EAAE,uBAAuB,CAAC,MAAM;AAC/C,oBAAA,QAAQ,EAAE,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAgET,EAAA,CAAA;AACF,iBAAA;;AAmED;AACA,SAAS,WAAW,CAAC,KAAwB,EAAE,IAAY,EAAA;IACzD,MAAM,KAAK,GAAG,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC;AACjC,IAAA,OAAO,KAAK,GAAG,CAAC,GAAG,MAAM,CAAC,gBAAgB,GAAG,KAAK;AACpD;;AC9KA;;;;;;;;AAQG;MAoEU,mBAAmB,CAAA;AACX,IAAA,KAAK,GAAG,MAAM,CAAC,gBAAgB,CAAC;AAChC,IAAA,UAAU,GAAG,MAAM,CAAC,oBAAoB,CAAC;AAC3C,IAAA,MAAM,GAAG,MAAM,CAAC,gBAAgB,CAAC;;AAGzC,IAAA,UAAU,GAAG,KAAK,CAAC,QAAQ,qDAAiB;AAE5C,IAAA,UAAU,GAAG,KAAK,CAAC,IAAI,sDAAC;AAEjC;;;AAGG;IACM,gBAAgB,GAAG,MAAM,EAAiB;IAEhC,aAAa,GAAG,kBAAkB;AAElC,IAAA,SAAS,GAAG,MAAM,CAAe,aAAa,qDAAC;AAEjD,IAAA,MAAM,GAAG,SAAS,CAAC,kBAAkB,kDAAC;AAEvD;;;;;AAKG;AACgB,IAAA,OAAO,GAAG,QAAQ,CAAC,MAAM,IAAI,CAAC,KAAK,CAAC,UAAU,EAAE,CAAC,OAAO,mDAAC;AAE5E;;;;;AAKG;AACgB,IAAA,eAAe,GAAG,QAAQ,CAA6B,MAAK;QAC7E,MAAM,QAAQ,GAAG,IAAI,CAAC,UAAU,CAAC,kBAAkB,EAAE;AACrD,QAAA,OAAO,QAAQ,KAAK,SAAS,IAAI,QAAQ,KAAK,IAAI,CAAC,OAAO,EAAE,GAAG,QAAQ,GAAG,SAAS;AACrF,IAAA,CAAC,2DAAC;AAEF;;;;;;;;;AASG;IACgB,MAAM,GAAG,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,UAAU,EAAE,CAAC,IAAI,IAAI,EAAE,EAAE,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC,EAAA,IAAA,SAAA,GAAA,CAAA,EAAA,SAAA,EAAA,QAAA,EAAA,CAAA,GAAA,EAAA,CAAA,CAAC;AAElG;;;;;;;;;;;;AAYG;AACgB,IAAA,eAAe,GAAG,QAAQ,CAAqB,MAAK;QACrE,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,UAAU,EAA+C;AACrF,QAAA,IAAI,OAAO,QAAQ,EAAE,aAAa,KAAK,QAAQ,EAAE;AAC/C,YAAA,OAAO,uBAAuB;QAChC;AACA,QAAA,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE;AAClB,YAAA,OAAO,6CAA6C;QACtD;AACA,QAAA,OAAO,SAAS;AAClB,IAAA,CAAC,2DAAC;IAEiB,OAAO,GAAG,QAAQ,CACnC,MACE,IAAI,CAAC,UAAU,CAAC,eAAe,EAAE;AACjC,QAAA,CAAC,IAAI,CAAC,UAAU,CAAC,QAAQ,EAAE;AAC3B,QAAA,IAAI,CAAC,KAAK,CAAC,OAAO,EAAE;AACpB,QAAA,IAAI,CAAC,MAAM,EAAE,EAAA,IAAA,SAAA,GAAA,CAAA,EAAA,SAAA,EAAA,SAAA,EAAA,CAAA,GAAA,EAAA,CAAA,CAChB;AAEkB,IAAA,SAAS,GAAG,QAAQ,CAAC,MAAM,WAAW,CAAC,IAAI,CAAC,KAAK,CAAC,UAAU,EAAE,CAAC,CAAC,MAAM,qDAAC;;AAGvE,IAAA,aAAa,GAAG,QAAQ,CAAC,MAAM,IAAI,CAAC,KAAK,CAAC,iBAAiB,EAAE,yDAAC;AAEjF,IAAA,WAAA,GAAA;;;QAGE,MAAM,CAAC,MAAK;AACV,YAAA,MAAM,QAAQ,GAAG,IAAI,CAAC,UAAU,EAAE;;;;;;;AAOlC,YAAA,SAAS,CAAC,MAAM,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;AAC5C,QAAA,CAAC,CAAC;;;QAIF,MAAM,CAAC,MAAK;YACV,MAAM,KAAK,GAAG,WAAW,CAAC,IAAI,CAAC,KAAK,CAAC,UAAU,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,OAAO,KAAK,OAAO,CAAC,IAAI,CAAC;YACjF,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,IAAI,oBAAoB,CAAC,KAAK,CAAC,EAAE;gBACrD;YACF;;;YAGA,SAAS,CAAC,MAAM,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;AACvC,QAAA,CAAC,CAAC;;QAGF,MAAM,CAAC,MAAK;AACV,YAAA,IAAI,CAAC,gBAAgB,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,UAAU,EAAE,CAAC;AACrD,QAAA,CAAC,CAAC;IACJ;;IAGU,aAAa,GAAA;AACrB,QAAA,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC;IACvB;AAEA;;;;AAIG;AACK,IAAA,SAAS,CAAC,SAAkB,EAAA;AAClC,QAAA,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,EAAE;QAC5B,IAAI,CAAC,MAAM,EAAE;YACX;QACF;AACA,QAAA,MAAM,SAAS,GAAG,IAAI,CAAC,MAAM,CAAC,MAAM,CAClC,MAAM,CAAC,WAAW,EAAE,EACpB,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,MAAM;YACtC,IAAI,EAAE,IAAI,CAAC,YAAY;YACvB,EAAE,EAAE,IAAI,CAAC,UAAU;YACnB,EAAE,EAAE,IAAI,CAAC,EAAE;SACZ,CAAC,CAAC,CACJ;QACD,IAAI,SAAS,EAAE;AACb,YAAA,IAAI,CAAC,KAAK,CAAC,qBAAqB,CAAC,SAAS,CAAC;YAC3C;QACF;AACA,QAAA,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC,SAAS,CAAC;IACtC;AAEU,IAAA,aAAa,CAAC,KAAkE,EAAA;QACxF,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,KAAK,CAAC,UAAU,EAAE,EAAE,CAAC,EAAE,KAAK,CAAC,CAAC,EAAE,CAAC,EAAE,KAAK,CAAC,CAAC,EAAE,CAAC;;;AAGlF,QAAA,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC;IACzB;AAEA;;;;;;;AAOG;AACO,IAAA,YAAY,CAAC,IAAY,EAAA;AACjC,QAAA,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC,IAAI,CAAC;QAC/B,IAAI,CAAC,MAAM,EAAE,EAAE,UAAU,CAAC,IAAI,CAAC;IACjC;AAEA;;;;AAIG;IACO,mBAAmB,GAAA;QAC3B,KAAK,MAAM,IAAI,IAAI,CAAC,GAAG,IAAI,CAAC,aAAa,EAAE,CAAC,EAAE;AAC5C,YAAA,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,IAAI,CAAC;QAC7B;IACF;;AAGU,IAAA,eAAe,CAAC,UAAiC,EAAA;QACzD,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,SAAS,EAAE;QACvC,IAAI,CAAC,GAAG,EAAE;QACV,KAAK,MAAM,QAAQ,IAAI,QAAQ,CAAC,MAAM,EAAE,EAAE;AACxC,YAAA,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC,GAAG,GAAG,CAAC;QACnC;AACA,QAAA,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,UAAU,EAAE,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC;AAC9D,QAAA,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC;IACzB;AAEA;;;;;AAKG;AACO,IAAA,SAAS,CAAC,KAAoB,EAAA;QACtC,IAAI,EAAE,KAAK,CAAC,OAAO,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,KAAK,CAAC,GAAG,CAAC,WAAW,EAAE,KAAK,GAAG,EAAE;YACxE;QACF;QACA,KAAK,CAAC,cAAc,EAAE;AACtB,QAAA,IAAI,IAAI,CAAC,OAAO,EAAE,EAAE;AAClB,YAAA,IAAI,CAAC,UAAU,CAAC,IAAI,EAAE;QACxB;IACF;AAEA;;;;AAIG;AACO,IAAA,UAAU,CAAC,KAA2B,EAAA;QAC9C,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC;;QAEjC,IAAI,CAAC,MAAM,EAAE,EAAE,UAAU,CAAC,KAAK,CAAC,QAAQ,CAAC;IAC3C;AAEA;;;;;;;;AAQG;AACM,IAAA,OAAO,GAAoB,IAAI,CAAC,KAAK,CAAC,OAAO;AAEtD;;;AAGG;IACH,SAAS,GAAA;AACP,QAAA,IAAI,CAAC,KAAK,CAAC,SAAS,EAAE;IACxB;wGA9OW,mBAAmB,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA;AAAnB,IAAA,OAAA,IAAA,GAAA,EAAA,CAAA,oBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,SAAA,EAAA,IAAA,EAAA,mBAAmB,EAAA,YAAA,EAAA,IAAA,EAAA,QAAA,EAAA,aAAA,EAAA,MAAA,EAAA,EAAA,UAAA,EAAA,EAAA,iBAAA,EAAA,YAAA,EAAA,UAAA,EAAA,YAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,IAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,UAAA,EAAA,EAAA,iBAAA,EAAA,YAAA,EAAA,UAAA,EAAA,YAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,EAAA,OAAA,EAAA,EAAA,gBAAA,EAAA,kBAAA,EAAA,EAAA,IAAA,EAAA,EAAA,SAAA,EAAA,EAAA,SAAA,EAAA,mBAAA,EAAA,EAAA,EAAA,SAAA,EAfnB;YACT,gBAAgB;YAChB,gBAAgB;YAChB,oBAAoB;YACpB,gBAAgB;YAChB,sBAAsB;AACvB,SAAA,EAAA,WAAA,EAAA,CAAA,EAAA,YAAA,EAAA,QAAA,EAAA,KAAA,EAAA,IAAA,EAAA,SAAA,EA6BmC,kBAAkB,EAAA,WAAA,EAAA,IAAA,EAAA,QAAA,EAAA,IAAA,EAAA,CAAA,EAAA,QAAA,EAAA,EAAA,EAAA,QAAA,EChGxD,46ZAwSA,EAAA,YAAA,EAAA,CAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EDxPI,kBAAkB,EAAA,QAAA,EAAA,YAAA,EAAA,MAAA,EAAA,CAAA,YAAA,CAAA,EAAA,OAAA,EAAA,CAAA,YAAA,EAAA,aAAA,CAAA,EAAA,EAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EAClB,4BAA4B,EAAA,QAAA,EAAA,uBAAA,EAAA,OAAA,EAAA,CAAA,UAAA,CAAA,EAAA,EAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EAC5B,sBAAsB,EAAA,QAAA,EAAA,iBAAA,EAAA,EAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EACtB,uBAAuB,EAAA,QAAA,EAAA,kBAAA,EAAA,OAAA,EAAA,CAAA,eAAA,CAAA,EAAA,EAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EACvB,uBAAuB,6DACvB,sBAAsB,EAAA,QAAA,EAAA,iBAAA,EAAA,CAAA,EAAA,eAAA,EAAA,EAAA,CAAA,uBAAA,CAAA,MAAA,EAAA,CAAA;;4FAuBb,mBAAmB,EAAA,UAAA,EAAA,CAAA;kBAhC/B,SAAS;+BACE,aAAa,EAAA,UAAA,EACX,IAAI,EAAA,OAAA,EACP;wBACP,kBAAkB;wBAClB,4BAA4B;wBAC5B,sBAAsB;wBACtB,uBAAuB;wBACvB,uBAAuB;wBACvB,sBAAsB;qBACvB,EAAA,eAAA,EACgB,uBAAuB,CAAC,MAAM,EAAA,SAAA,EAMpC;wBACT,gBAAgB;wBAChB,gBAAgB;wBAChB,oBAAoB;wBACpB,gBAAgB;wBAChB,sBAAsB;AACvB,qBAAA,EAAA,IAAA,EAMK,EAAE,WAAW,EAAE,mBAAmB,EAAE,EAAA,QAAA,EAAA,46ZAAA,EAAA;8XAuBN,kBAAkB,CAAA,EAAA,EAAA,QAAA,EAAA,IAAA,EAAA,CAAA,EAAA,CAAA,EAAA,EAAA,CAAA;;AEhGxD;;;;;;;;;;;;;AAaG;MA0BU,mBAAmB,CAAA;AACX,IAAA,KAAK,GAAG,MAAM,CAAC,eAAe,CAAC;AACjC,IAAA,QAAQ,GAAG,MAAM,CAAC,QAAQ,CAAC;AAE5C;;;AAGG;IACM,IAAI,GAAG,MAAM,EAAU;IAEb,aAAa,GAAG,kBAAkB;;AAGlC,IAAA,MAAM,GAAG,MAAM,CAAC,EAAE,kDAAC;AAEnB,IAAA,OAAO,GAAG,MAAM,CAAC,EAAE,mDAAC;AACpB,IAAA,QAAQ,GAAG,MAAM,CAAC,EAAE,oDAAC;AAExC;;;AAGG;AACgB,IAAA,aAAa,GAAG,MAAM,CAAgB,IAAI,yDAAC;;AAG3C,IAAA,eAAe,GAAG,MAAM,CAAgB,IAAI,2DAAC;AAC7C,IAAA,aAAa,GAAG,MAAM,CAAC,EAAE,yDAAC;AAC1B,IAAA,cAAc,GAAG,MAAM,CAAC,EAAE,0DAAC;AAE7B,IAAA,kBAAkB,GACjC,SAAS,CAA+B,oBAAoB,8DAAC;AAE/D;;;AAGG;AACgB,IAAA,OAAO,GAAG,QAAQ,CAAkC,MAAK;AAC1E,QAAA,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC,iBAAiB,EAAE;AACvD,QAAA,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE;AACvB,YAAA,OAAO,IAAI,CAAC,KAAK,CAAC,SAAS,EAAE;QAC/B;QACA,OAAO,IAAI,CAAC;AACT,aAAA,SAAS;AACT,aAAA,MAAM,CACL,CAAC,OAAO,KACN,OAAO,CAAC,IAAI,CAAC,iBAAiB,EAAE,CAAC,QAAQ,CAAC,MAAM,CAAC;YACjD,OAAO,CAAC,KAAK,CAAC,iBAAiB,EAAE,CAAC,QAAQ,CAAC,MAAM,CAAC,CACrD;AACL,IAAA,CAAC,mDAAC;;IAGiB,eAAe,GAAG,QAAQ,CAC3C,MAAM,IAAI,CAAC,KAAK,CAAC,SAAS,EAAE,CAAC,MAAM,GAAG,CAAC,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC,MAAM,KAAK,CAAC,EAAA,IAAA,SAAA,GAAA,CAAA,EAAA,SAAA,EAAA,iBAAA,EAAA,CAAA,GAAA,EAAA,CAAA,CACvE;AAED;;;AAGG;IACgB,YAAY,GAAG,QAAQ,CACxC,MAAM,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,IAAI,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,gBAAgB,EAAA,IAAA,SAAA,GAAA,CAAA,EAAA,SAAA,EAAA,cAAA,EAAA,CAAA,GAAA,EAAA,CAAA,CAC/E;IAEkB,eAAe,GAAG,QAAQ,CAAC,MAC5C,IAAI,CAAC,YAAY;AACf,UAAE;UACA,uFAAuF,EAAA,IAAA,SAAA,GAAA,CAAA,EAAA,SAAA,EAAA,iBAAA,EAAA,CAAA,GAAA,EAAA,CAAA,CAC5F;;AAGkB,IAAA,YAAY,GAAG,QAAQ,CACxC,MACE,IAAI,CAAC,KAAK,CAAC,aAAa,CAAC,WAAW;AACpC,QAAA,CAAC,IAAI,CAAC,KAAK,CAAC,UAAU,EAAE;QACxB,IAAI,CAAC,OAAO,EAAE,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC;QAChC,IAAI,CAAC,QAAQ,EAAE,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC,EAAA,IAAA,SAAA,GAAA,CAAA,EAAA,SAAA,EAAA,cAAA,EAAA,CAAA,GAAA,EAAA,CAAA,CACpC;IAEkB,kBAAkB,GAAG,QAAQ,CAC9C,MACE,IAAI,CAAC,YAAY,EAAE;AACnB,QAAA,CAAC,IAAI,CAAC,KAAK,CAAC,UAAU,EAAE;QACxB,IAAI,CAAC,aAAa,EAAE,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC;QACtC,IAAI,CAAC,cAAc,EAAE,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC,EAAA,IAAA,SAAA,GAAA,CAAA,EAAA,SAAA,EAAA,oBAAA,EAAA,CAAA,GAAA,EAAA,CAAA,CAC1C;AAES,IAAA,MAAM,CAAC,IAAY,EAAA;AAC3B,QAAA,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC;IACtB;IAEU,OAAO,GAAA;AACf,QAAA,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,IAAI,CAAC;AAC5B,QAAA,IAAI,CAAC,KAAK,CAAC,OAAO,EAAE;IACtB;AAEA;;;AAGG;IACO,SAAS,GAAA;AACjB,QAAA,IAAI,CAAC,IAAI,CAAC,YAAY,EAAE,EAAE;YACxB;QACF;QACA,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,kBAAkB,EAAE;AAC/C,QAAA,IAAI,OAAO,KAAK,SAAS,EAAE;;;YAGzB;QACF;AACA,QAAA,IAAI,CAAC;AACF,aAAA,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,IAAI,EAAE,EAAE,IAAI,CAAC,QAAQ,EAAE,CAAC,IAAI,EAAE,EAAE,OAAO;AAC7D,aAAA,SAAS,CAAC,CAAC,OAAO,KAAI;AACrB,YAAA,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;AACpB,YAAA,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;YACrB,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC;AAC9B,QAAA,CAAC,CAAC;IACN;AAEU,IAAA,cAAc,CAAC,OAA6B,EAAA;AACpD,QAAA,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,IAAI,CAAC;QAC5B,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC;QACtC,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,CAAA,EAAG,OAAO,CAAC,IAAI,CAAA,KAAA,CAAO,CAAC;QAC9C,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,CAAA,EAAG,OAAO,CAAC,KAAK,CAAA,QAAA,CAAU,CAAC;;;AAGnD,QAAA,eAAe,CAAC,MAAM,IAAI,CAAC,kBAAkB,EAAE,EAAE,aAAa,CAAC,KAAK,EAAE,EAAE;YACtE,QAAQ,EAAE,IAAI,CAAC,QAAQ;AACxB,SAAA,CAAC;IACJ;IAEU,eAAe,GAAA;AACvB,QAAA,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,IAAI,CAAC;IAChC;IAEU,gBAAgB,GAAA;AACxB,QAAA,MAAM,MAAM,GAAG,IAAI,CAAC,eAAe,EAAE;QACrC,IAAI,MAAM,KAAK,IAAI,IAAI,CAAC,IAAI,CAAC,kBAAkB,EAAE,EAAE;YACjD;QACF;AACA,QAAA,IAAI,CAAC;AACF,aAAA,SAAS,CAAC,MAAM,EAAE,IAAI,CAAC,aAAa,EAAE,CAAC,IAAI,EAAE,EAAE,IAAI,CAAC,cAAc,EAAE,CAAC,IAAI,EAAE;aAC3E,SAAS,CAAC,MAAK;;;AAGd,YAAA,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,IAAI,CAAC;AAChC,QAAA,CAAC,CAAC;IACN;AAEU,IAAA,SAAS,CAAC,IAAY,EAAA;AAC9B,QAAA,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,IAAI,CAAC;AAC9B,QAAA,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,IAAI,CAAC;IAC9B;IAEU,YAAY,GAAA;AACpB,QAAA,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,IAAI,CAAC;IAC9B;AAEU,IAAA,aAAa,CAAC,IAAY,EAAA;AAClC,QAAA,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,IAAI,CAAC;AAC5B,QAAA,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC;IACzB;;AAGU,IAAA,UAAU,CAAC,KAAa,EAAA;AAChC,QAAA,MAAM,MAAM,GAAG,IAAI,IAAI,CAAC,KAAK,CAAC;QAC9B,OAAO,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC,GAAG,KAAK,GAAG,MAAM,CAAC,kBAAkB,EAAE;IAC7E;wGAtKW,mBAAmB,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA;AAAnB,IAAA,OAAA,IAAA,GAAA,EAAA,CAAA,oBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,SAAA,EAAA,IAAA,EAAA,mBAAmB,EAAA,YAAA,EAAA,IAAA,EAAA,QAAA,EAAA,aAAA,EAAA,OAAA,EAAA,EAAA,IAAA,EAAA,MAAA,EAAA,EAAA,SAAA,EAHnB,CAAC,eAAe,CAAC,oKCpC9B,4vXAwRA,EAAA,eAAA,EAAA,EAAA,CAAA,uBAAA,CAAA,MAAA,EAAA,CAAA;;4FDjPa,mBAAmB,EAAA,UAAA,EAAA,CAAA;kBAP/B,SAAS;+BACE,aAAa,EAAA,UAAA,EACX,IAAI,EAAA,eAAA,EACC,uBAAuB,CAAC,MAAM,EAAA,SAAA,EACpC,CAAC,eAAe,CAAC,EAAA,QAAA,EAAA,4vXAAA,EAAA;6HAiCc,oBAAoB,EAAA,EAAA,QAAA,EAAA,IAAA,EAAA,CAAA,EAAA,CAAA,EAAA,EAAA,CAAA;;AErEhE;;;;;;AAMG;AAEH;;ACRA;;AAEG;;;;"}
|
|
1
|
+
{"version":3,"file":"esfaenza-dpe-builder.mjs","sources":["../../../projects/dpe-builder/src/lib/api/dpe-api-error.ts","../../../projects/dpe-builder/src/lib/api/dpe-builder-api.ts","../../../projects/dpe-builder/src/lib/api/http-dpe-builder-api.ts","../../../projects/dpe-builder/src/lib/model/dpe-definition.model.ts","../../../projects/dpe-builder/src/lib/model/dpe-diagnostics.model.ts","../../../projects/dpe-builder/src/lib/model/dpe-api.model.ts","../../../projects/dpe-builder/src/lib/core/dpe-capabilities.ts","../../../projects/dpe-builder/src/lib/core/dpe-graph.ts","../../../projects/dpe-builder/src/lib/core/dpe-node-collection-guard.ts","../../../projects/dpe-builder/src/lib/core/dpe-diagnostics.util.ts","../../../projects/dpe-builder/src/lib/core/dpe-node-layout.ts","../../../projects/dpe-builder/src/lib/core/dpe-node-catalog.ts","../../../projects/dpe-builder/src/lib/core/dpe-node-factory.ts","../../../projects/dpe-builder/src/lib/core/dpe-document.store.ts","../../../projects/dpe-builder/src/lib/core/dpe-catalog.store.ts","../../../projects/dpe-builder/src/lib/core/dpe-conditional-fields.ts","../../../projects/dpe-builder/src/lib/core/dpe-formula-catalog.view.ts","../../../projects/dpe-builder/src/lib/core/dpe-validation.service.ts","../../../projects/dpe-builder/src/lib/core/dpe-formula-catalog.store.ts","../../../projects/dpe-builder/src/lib/core/dpe-layout.service.ts","../../../projects/dpe-builder/src/lib/core/dpe-metadata.store.ts","../../../projects/dpe-builder/src/lib/core/dpe-node-icons.ts","../../../projects/dpe-builder/src/lib/core/dpe-viewport.ts","../../../projects/dpe-builder/src/lib/ui/shared/dpe-node-icon.component.ts","../../../projects/dpe-builder/src/lib/ui/canvas/dpe-canvas.model.ts","../../../projects/dpe-builder/src/lib/ui/canvas/dpe-canvas.component.ts","../../../projects/dpe-builder/src/lib/ui/canvas/dpe-canvas.component.html","../../../projects/dpe-builder/src/lib/ui/diagnostics/dpe-diagnostics-panel.component.ts","../../../projects/dpe-builder/src/lib/ui/inspector/dpe-node-facts.component.ts","../../../projects/dpe-builder/src/lib/ui/shared/dpe-select-value.directive.ts","../../../projects/dpe-builder/src/lib/ui/inspector/dpe-node-inspector-base.ts","../../../projects/dpe-builder/src/lib/ui/inspector/dpe-aggregate-inspector.component.ts","../../../projects/dpe-builder/src/lib/ui/inspector/dpe-append-inspector.component.ts","../../../projects/dpe-builder/src/lib/ui/inspector/dpe-atomic-writeback-inspector.component.ts","../../../projects/dpe-builder/src/lib/ui/shared/dpe-column-type-editor.component.ts","../../../projects/dpe-builder/src/lib/ui/inspector/dpe-custom-node-inspector.component.ts","../../../projects/dpe-builder/src/lib/ui/inspector/dpe-data-source-inspector.component.ts","../../../projects/dpe-builder/src/lib/ui/inspector/dpe-filter-inspector.component.ts","../../../projects/dpe-builder/src/lib/ui/inspector/dpe-forecast-inspector.component.ts","../../../projects/dpe-builder/src/lib/ui/inspector/dpe-hierarchy-path-inspector.component.ts","../../../projects/dpe-builder/src/lib/ui/inspector/dpe-join-inspector.component.ts","../../../projects/dpe-builder/src/lib/ui/shared/dpe-expression-editor.component.ts","../../../projects/dpe-builder/src/lib/ui/inspector/dpe-transform-inspector.component.ts","../../../projects/dpe-builder/src/lib/ui/inspector/dpe-writeback-inspector.component.ts","../../../projects/dpe-builder/src/lib/ui/inspector/dpe-node-inspector.component.ts","../../../projects/dpe-builder/src/lib/ui/dialog/dpe-node-dialog.component.ts","../../../projects/dpe-builder/src/lib/ui/palette/dpe-node-palette.component.ts","../../../projects/dpe-builder/src/lib/ui/preview/dpe-sql-preview.component.ts","../../../projects/dpe-builder/src/lib/ui/schema/dpe-schema-panel.component.ts","../../../projects/dpe-builder/src/lib/ui/dpe-builder.component.ts","../../../projects/dpe-builder/src/lib/ui/dpe-builder.component.html","../../../projects/dpe-builder/src/lib/ui/catalog/dpe-catalog.component.ts","../../../projects/dpe-builder/src/lib/ui/catalog/dpe-catalog.component.html","../../../projects/dpe-builder/src/public-api.ts","../../../projects/dpe-builder/src/esfaenza-dpe-builder.ts"],"sourcesContent":["/**\r\n * Errore categorizzato del contratto verso il backend.\r\n *\r\n * L'editor si comporta in base alla **categoria**, mai leggendo il messaggio: chi implementa\r\n * {@link DpeBuilderApi} traduce l'errore di trasporto (status HTTP, payload, timeout) in un\r\n * `DpeApiError`, e da li' in poi nella libreria non esiste piu' nessuna nozione di HTTP\r\n * (ADR 0007).\r\n */\r\n\r\n/**\r\n * Le categorie sono **tre** e chiuse, perche' sono tre comportamenti diversi della UI:\r\n *\r\n * - `MissingService`: la primitiva non e' implementata su questo ambiente. Degrada il\r\n * **singolo comando** (l'anteprima SQL si disabilita), non l'editor;\r\n * - `Transport`: la chiamata non e' arrivata o non e' tornata. Si puo' ritentare;\r\n * - `Backend`: il backend ha risposto rifiutando. Non si ritenta da soli.\r\n *\r\n * Un errore di validazione **non e'** una categoria: le diagnostiche sono un *risultato*\r\n * (`DpeValidateResult.diagnostics`), non un fallimento — e' la stessa regola del backend,\r\n * dove il fallimento previsto e' un risultato e non un'eccezione.\r\n */\r\nexport type DpeApiErrorCategory = 'MissingService' | 'Transport' | 'Backend';\r\n\r\nexport class DpeApiError extends Error {\r\n constructor(\r\n readonly category: DpeApiErrorCategory,\r\n message: string,\r\n /** Payload di dettaglio dell'host, opaco per la libreria. */\r\n readonly details?: unknown,\r\n ) {\r\n super(message);\r\n this.name = 'DpeApiError';\r\n }\r\n\r\n static is(value: unknown): value is DpeApiError {\r\n return value instanceof DpeApiError;\r\n }\r\n\r\n /** Vero quando il comando corrispondente va disabilitato invece che segnalato. */\r\n static isMissingService(value: unknown): boolean {\r\n return DpeApiError.is(value) && value.category === 'MissingService';\r\n }\r\n}\r\n\r\n/** Messaggi di ripiego: il `message` dell'host, quando c'e', ha la precedenza. */\r\nexport const DPE_API_ERROR_FALLBACK_MESSAGE: Record<DpeApiErrorCategory, string> = {\r\n MissingService: 'Funzione non configurata su questo ambiente.',\r\n Transport: 'Il backend non e’ raggiungibile.',\r\n Backend: 'Il backend ha rifiutato la richiesta.',\r\n};\r\n","/**\r\n * L'unica porta verso il backend — `docs/contracts.md` §7.\r\n *\r\n * **Non esiste un documento di endpoint: il contratto e' questa classe astratta** (ADR 0007).\r\n * Fa anche da token di dependency injection: chi integra l'editor fornisce una\r\n * implementazione e la registra come provider di `DpeBuilderApi`.\r\n *\r\n * Due conseguenze da non aggirare:\r\n *\r\n * 1. **questo file non conosce HTTP.** Nessun `HttpClient`, nessun `fetch`, nessun URL qui ne'\r\n * in nessuno store o componente. L'ADR 0020 ha spostato il client HTTP **dentro** la libreria\r\n * (`lib/api/http-dpe-builder-api.ts`), quindi la regola non e' piu' «zero HTTP in\r\n * `projects/dpe-builder`» ma «zero HTTP fuori da quel file»: e' *una* implementazione fra le\r\n * possibili, non la definizione del contratto, che resta questa classe;\r\n * 2. **un dato nuovo dal backend e' un metodo nuovo qui**, mai una chiamata dentro un\r\n * componente.\r\n *\r\n * I metodi con implementazione di default rifiutano con `DpeApiError('MissingService', ...)`:\r\n * un ambiente che non li espone lascia l'editor funzionante con il singolo comando\r\n * disabilitato. Un'anteprima SQL non disponibile disabilita l'anteprima, non l'editor.\r\n *\r\n * I metodi restituiscono `Observable` perche' l'I/O del frontend e' asincrono per natura. Il\r\n * vincolo di sincronia della libreria C# riguarda il backend: qui si traduce solo nel fatto\r\n * che una chiamata occupa il chiamante finche' non termina, quindi chi la invoca mostra\r\n * l'attesa e non incatena una validazione a ogni battitura senza debounce.\r\n */\r\n\r\nimport { Observable, throwError } from 'rxjs';\r\n\r\nimport { DpeDefinition } from '../model/dpe-definition.model';\r\nimport {\r\n DpeCompileResult,\r\n DpeDefinitionSummary,\r\n DpeEnvironment,\r\n DpeFormulaCatalogResult,\r\n DpeParameterValue,\r\n DpeValidateResult,\r\n TableLookupResult,\r\n TableSummary,\r\n} from '../model/dpe-api.model';\r\nimport { DpeApiError } from './dpe-api-error';\r\n// Import di **solo tipo**: `core/dpe-capabilities` importa questa classe, e senza `import type`\r\n// il ciclo sarebbe reale a runtime. Il tipo si cancella in compilazione, il ciclo no.\r\nimport type { DpeOptionalPrimitive } from '../core/dpe-capabilities';\r\n\r\n/** Ripiego dei metodi opzionali non implementati dall'host. */\r\nfunction missing(primitive: string): Observable<never> {\r\n return throwError(\r\n () => new DpeApiError('MissingService', `Primitiva non disponibile su questo ambiente: ${primitive}.`),\r\n );\r\n}\r\n\r\nexport abstract class DpeBuilderApi {\r\n /**\r\n * Primitive opzionali che **questo ambiente non serve**, dichiarate dall'ospite (ADR 0020,\r\n * `contracts.md` §7).\r\n *\r\n * Perche' esiste, dato che la rilevazione per identita' del metodo bastava: da quando la\r\n * libreria spedisce un client HTTP che implementa **tutte** le primitive\r\n * (`HttpDpeBuilderApi`), chi lo eredita non ha modo di *dis*-sovrascrivere un metodo. Si\r\n * ritroverebbe comandi attivi su endpoint che il suo backend non serve — esattamente il\r\n * difetto che la regola dell'identita' esisteva per prevenire.\r\n *\r\n * E' **solo sottrattiva**: non rende disponibile cio' che non e' implementato, e il tipo\r\n * `DpeOptionalPrimitive` le impedisce di nominare le tre primitive obbligatorie. Vuoto per\r\n * default, cosi' un'implementazione che non la tocca conserva esattamente il comportamento\r\n * di prima.\r\n *\r\n * Chi la usa deve anche rigettare la primitiva con `DpeApiError('MissingService', ...)`\r\n * **senza emettere la richiesta**: sapere in anticipo che un servizio non c'e' e chiederglielo\r\n * comunque e' il difetto che questa dichiarazione evita.\r\n */\r\n readonly unsupportedPrimitives: readonly DpeOptionalPrimitive[] = [];\r\n\r\n /**\r\n * `IDpeService.Validate` — **non produce SQL e non legge dati** (`contracts.md` §5). E' la\r\n * primitiva del feedback durante l'editing: diagnostiche, schemi di ogni nodo, ordine di\r\n * esecuzione e sequenza dei writeback in una sola risposta.\r\n *\r\n * Prende la definizione **in lavorazione**, non quella salvata: cio' che l'editor mostra\r\n * deve riferirsi a cio' che l'utente sta scrivendo. Va invocata con debounce.\r\n *\r\n * **`schemas` porta anche `requiredForm`**, la forma che i siti di espressione di quel nodo\r\n * pretendono. Sta li' e non sul risultato del catalogo delle formule perche' e' cio' che la\r\n * libreria **deriva** sul nodo, come le colonne e la compilabilita', mentre il catalogo e' cio'\r\n * che il compilatore **offre** e arriva solo quando il compilatore risponde: un ospite che\r\n * dichiara `providesFormulaCatalog` falso non fa chiamare quella rotta, e la forma non arrivava\r\n * mai. Chi implementa questo metodo la valorizza: senza, un campo formula non dice cosa vuole e\r\n * non si blocca quando il compilatore non sa produrre quella forma.\r\n */\r\n abstract validate(definition: DpeDefinition): Observable<DpeValidateResult>;\r\n\r\n /**\r\n * `IDpeService.GetTables` — passaggio diretto al provider di metadati dell'host, **non ai\r\n * cataloghi del database** (`contracts.md` §2). L'elenco puo' essere grande: chi lo consuma\r\n * carica in modo asincrono e filtra, non lo riversa in un `<select>`.\r\n */\r\n abstract getTables(): Observable<TableSummary[]>;\r\n\r\n /**\r\n * `IDpeService.GetTable` — i **tre** esiti dell'ADR 0004 restano distinti fino alla UI:\r\n * `Declared`, `DeclaredWithoutFields` (\"la tabella c'e', i campi non li so\") e `Unknown`.\r\n * Appiattirli e' esattamente cio' che il contratto vieta.\r\n */\r\n abstract getTable(tableName: string): Observable<TableLookupResult>;\r\n\r\n /**\r\n * `IDpeService.Compile` — l'anteprima dell'SQL generato, in sola lettura.\r\n *\r\n * Opzionale: senza di essa il comando di anteprima si disabilita e il resto dell'editor\r\n * continua a funzionare.\r\n *\r\n * `parameterValues` e' opzionale e corrisponde a `DpeCompileRequest.ParameterValues`.\r\n * Ometterlo e' un comportamento **dichiarato**, non un ripiego: l'anteprima usa allora i\r\n * `defaultValue` dei parametri. Passarli serve perche' senza di essi l'anteprima resterebbe\r\n * muta proprio sui parametri **privi** di default, cioe' quelli che l'utente vuole provare.\r\n */\r\n compilePreview(\r\n definition: DpeDefinition,\r\n parameterValues?: DpeParameterValue[],\r\n ): Observable<DpeCompileResult> {\r\n void definition;\r\n void parameterValues;\r\n return missing('compilePreview');\r\n }\r\n\r\n /**\r\n * Caricamento di una definizione per nome. Opzionale: dove non c'e', l'editor lavora sulla\r\n * definizione che gli viene passata dall'ospite.\r\n *\r\n * La deserializzazione (`IDpeService.Deserialize`) resta del backend: qui arriva un\r\n * documento gia' letto, con le sue eventuali diagnostiche gia' emesse.\r\n */\r\n load(name: string): Observable<DpeDefinition> {\r\n void name;\r\n return missing('load');\r\n }\r\n\r\n /** Salvataggio. Opzionale: un editor in sola lettura e' un uso legittimo. */\r\n save(definition: DpeDefinition): Observable<void> {\r\n void definition;\r\n return missing('save');\r\n }\r\n\r\n /**\r\n * La dichiarazione dell'ambiente (ADR 0017). Oggi porta **solo** il dialetto del database di\r\n * destinazione, che e' un fatto dell'installazione e non una preferenza dell'autore della\r\n * definizione: per questo l'editor non offre nessun comando per cambiarlo.\r\n *\r\n * Cosa l'editor fa con la risposta, e cosa non fa: mostra il dialetto in sola lettura e, se\r\n * differisce da quello della definizione (ADR 0009: e' li' che vive, ed e' da li' che il\r\n * backend lo legge), **lo segnala senza correggere**. Riscrivere `definition.dialect` sarebbe\r\n * una migrazione silenziosa delle capacita' con cui la definizione verra' compilata, per giunta eseguita\r\n * all'apertura e capace di marcare come «modificata» una definizione che nessuno ha toccato.\r\n *\r\n * Opzionale come le altre: dove manca, l'editor mostra il dialetto della definizione e non\r\n * dice nient'altro.\r\n */\r\n getEnvironment(): Observable<DpeEnvironment> {\r\n return missing('getEnvironment');\r\n }\r\n\r\n /**\r\n * `IDpeService.GetFormulaCatalog` — cosa il linguaggio di formule mette a disposizione **nel\r\n * nodo indicato** (ADR 0031/0036). Opzionale: senza, l'editor mostra un campo di testo semplice,\r\n * che e' esattamente cio' che mostrava prima che il catalogo esistesse.\r\n *\r\n * **Prende la definizione e il nome del nodo, non uno schema.** Il catalogo dipende dallo schema\r\n * a monte — i simboli `Column` sono le colonne che quel nodo vede — e risolvere uno schema e' del\r\n * backend (**I4**): il frontend non sa farlo e non deve imparare. La definizione e' quella **in\r\n * lavorazione**, perche' quella aperta nell'editor puo' non essere ancora salvata.\r\n *\r\n * **Non e' una chiamata leggera** (`frontend.md` §5-quater, normativo): lato libreria costa una\r\n * passata completa di grafo e di propagazione, e interroga il provider di metadati per tutti i\r\n * datasource della definizione. Chi la consuma la chiede **una volta per nodo** e la conserva —\r\n * `DpeFormulaCatalogStore` lo fa — e non a ogni apertura di un campo, che e' il modo naturale di\r\n * scriverlo e quello sbagliato.\r\n *\r\n * Si interroga `DpeEnvironment.providesFormulaCatalog` **prima** di chiamare: un compilatore che\r\n * non descrive il proprio linguaggio risponde un catalogo vuoto, e farsi pagare quella passata di\r\n * grafo per riceverlo e' spreco puro (ADR 0036 §5).\r\n *\r\n * **Restituisce l'involucro, non il catalogo nudo** (`contracts.md` §7). Il service distingue\r\n * tre esiti e il filo ne consegnava due, quindi un dialetto non coperto e un backend rotto\r\n * arrivavano qui identici: la differenza fra «non lo so» e «non si puo'» si perdeva prima di\r\n * raggiungere lo schermo. Chi implementa questo metodo non trasforma un esito con diagnostiche\r\n * in un errore di trasporto: le diagnostiche **sono** la risposta.\r\n *\r\n * **`requiredForm` non e' piu' in questa risposta**: sta su `DpeNodeSchemaInfo.requiredForm`,\r\n * cioe' negli schemi di `validate`. La ragione e' proprio la riga qui sopra — questa risposta\r\n * arriva solo quando il compilatore parla, e la forma la libreria la sa senza chiedere niente a\r\n * nessuno. Chi implementa questo metodo non la rimette qui: due copie dello stesso fatto\r\n * divergono (ADR 0018).\r\n */\r\n getFormulaCatalog(\r\n definition: DpeDefinition,\r\n nodeName: string,\r\n ): Observable<DpeFormulaCatalogResult> {\r\n void definition;\r\n void nodeName;\r\n return missing('getFormulaCatalog');\r\n }\r\n\r\n /**\r\n * Elenco delle definizioni disponibili — **catalogo** (ADR 0019). Opzionale: dove manca, un\r\n * catalogo montato dall'ospite mostra il perche' invece di un elenco vuoto.\r\n *\r\n * **Nessun oggetto di interrogazione**, di proposito: niente ricerca, niente paginazione. Il\r\n * filtro di testo del catalogo e' locale sui sommari ricevuti, e aggiungere una query in\r\n * seguito e' additivo. Un parametro che nessuno esercita e' un parametro che nessuno verifica.\r\n *\r\n * L'ospite e' tenuto a restituire i sommari in un **ordine stabile**: il catalogo non riordina,\r\n * perche' riordinare lato client combatterebbe una scelta che l'ospite ha gia' fatto.\r\n */\r\n listDefinitions(): Observable<DpeDefinitionSummary[]> {\r\n return missing('listDefinitions');\r\n }\r\n\r\n /**\r\n * Creazione di una definizione. Opzionale.\r\n *\r\n * **Restituisce il sommario, non `void`**: l'ospite puo' aver normalizzato il nome, e chi crea\r\n * apre **il nome tornato**, non quello digitato. Il risultato di una scrittura non si crede\r\n * sulla parola.\r\n *\r\n * Distinta da `save` per una ragione di merito: `save(definition)` significa «scrivi questa\r\n * definizione» e non distingue inserimento da aggiornamento, quindi farle significare anche\r\n * «creala» toglierebbe all'ospite l'unico posto in cui puo' **rifiutare un nome duplicato**.\r\n *\r\n * Non esiste una primitiva di duplicazione: duplicare e' `load` piu' `createDefinition`, e il\r\n * comando vive nel catalogo.\r\n */\r\n createDefinition(definition: DpeDefinition): Observable<DpeDefinitionSummary> {\r\n void definition;\r\n return missing('createDefinition');\r\n }\r\n\r\n /**\r\n * Cancellazione per nome. Opzionale: un catalogo di sola lettura e' un uso legittimo.\r\n *\r\n * Non esiste una primitiva di rinomina: rinominare cambia la chiave, quindi e'\r\n * creare-e-cancellare, cioe' una decisione di storage che e' dell'ospite (ADR 0019).\r\n */\r\n deleteDefinition(name: string): Observable<void> {\r\n void name;\r\n return missing('deleteDefinition');\r\n }\r\n}\r\n","/**\r\n * Implementazione HTTP di {@link DpeBuilderApi}, sulle rotte **normative** di\r\n * `docs/contracts.md` §7-bis.\r\n *\r\n * Vive nella libreria per decisione del committente (ADR 0020, che revisiona la sola clausola\r\n * dell'ADR 0007 sulla collocazione): l'integrazione e' due righe invece che un file da copiare.\r\n * Il prezzo, accettato e scritto: **le rotte sono contratto pubblico**, e cambiarne una e' un\r\n * breaking change. Da qui in poi questa classe non e' «un esempio» — una classe esportata da una\r\n * libreria viene usata in produzione a prescindere da come la chiamiamo nel commento, e chiamarla\r\n * esempio servirebbe solo a esentarla dai test.\r\n *\r\n * Resta vero cio' che conta dell'ADR 0007: **il contratto e' la classe astratta**, non questo\r\n * client. Nessun componente e nessuno store della libreria lo importa. Chi ha rotte diverse ha due\r\n * strade, e nessuna tocca l'editor:\r\n *\r\n * 1. **estendere questa classe** e sovrascrivere i soli metodi divergenti. I punti di estensione\r\n * sono `protected` di proposito: `url()` per il prefisso e la forma del percorso, `segment()`\r\n * per la codifica, i quattro verbi per il trasporto, `fail()` per la traduzione degli errori,\r\n * `request()` per cio' che avvolge ogni chiamata;\r\n * 2. **scrivere un'altra implementazione di `DpeBuilderApi` da zero** — legittima e supportata.\r\n *\r\n * Le tre cose che chi legge questo file deve sapere, e che nessun compilatore gli dira':\r\n *\r\n * 1. **il corpo e' il documento nudo** dove il backend analizza o archivia la definizione\r\n * (`validate`, `createDefinition`, `save`). L'unico involucro e' `compilePreview`, perche' li'\r\n * il corpo porta anche i valori dei parametri. Sbagliare involucro **non produce nessun\r\n * errore**: un backend che deserializza una `DpeDefinition` da `{definition: …}` non riconosce\r\n * nessuna proprieta', risponde `200`, e l'editor mostra la validazione di una definizione\r\n * vuota;\r\n * 2. **la serializzazione e' camelCase.** I tipi TypeScript sono allineati a mano ai record C#: un\r\n * backend che risponde in PascalCase non produce nessun errore, produce campi `undefined`;\r\n * 3. **l'errore di trasporto diventa una categoria.** L'editor si comporta in base alla categoria\r\n * e non legge mai il messaggio: `MissingService` disabilita un comando, `Transport` e'\r\n * ritentabile, `Backend` no.\r\n *\r\n * Cosa questo client **non** fa, e non per dimenticanza: nessun `retry`, nessun `shareReplay`,\r\n * nessuna cache, nessun debounce, nessuna intestazione propria. Traduce trasporto ed errori e\r\n * nient'altro — la cache vive negli store, il debounce nel chiamante, i tentativi e\r\n * l'autenticazione negli interceptor dell'ospite. E non tocca `definition.dialect` in nessun\r\n * metodo (ADR 0009 e 0017): sarebbe una migrazione silenziosa delle capacita' con cui il backend\r\n * compilera' la definizione.\r\n */\r\n\r\nimport { HttpClient, HttpErrorResponse } from '@angular/common/http';\r\nimport {\r\n EnvironmentProviders,\r\n InjectionToken,\r\n Injectable,\r\n inject,\r\n makeEnvironmentProviders,\r\n} from '@angular/core';\r\nimport { Observable, catchError, defer, map, throwError } from 'rxjs';\r\n\r\nimport { DpeBuilderApi } from './dpe-builder-api';\r\nimport { DpeApiError, DpeApiErrorCategory } from './dpe-api-error';\r\nimport { DpeDefinition } from '../model/dpe-definition.model';\r\nimport {\r\n DpeCompileResult,\r\n DpeDefinitionSummary,\r\n DpeEnvironment,\r\n DpeFormulaCatalogResult,\r\n DpeParameterValue,\r\n DpeValidateResult,\r\n TableLookupResult,\r\n TableSummary,\r\n} from '../model/dpe-api.model';\r\nimport type { DpeOptionalPrimitive } from '../core/dpe-capabilities';\r\n\r\n/**\r\n * Configurazione del client. Iniettata e non cablata: in una libreria una costante non e' una\r\n * scelta, e `const BASE = '/api/dpe'` in un file di libreria toglierebbe all'ospite la sua.\r\n */\r\nexport interface DpeBuilderHttpConfig {\r\n /** Prefisso comune degli endpoint, es. `/api/dpe` o `https://host/api/dpe`. Senza slash finale. */\r\n baseUrl?: string;\r\n /**\r\n * Primitive opzionali che questo ambiente **non serve** (`contracts.md` §7, dichiarazione\r\n * sottrattiva). E' l'unico modo che ha l'ospite di spegnere una primitiva che questo client\r\n * implementa per forza: conoscendo tutti e undici gli endpoint, li dichiarerebbe altrimenti tutti\r\n * disponibili, e un comando attivo su un endpoint non servito fallisce alla pressione.\r\n */\r\n unsupported?: readonly DpeOptionalPrimitive[];\r\n}\r\n\r\n/** Base di default delle rotte, in un solo posto: token e funzione di registrazione la condividono. */\r\nconst DPE_BUILDER_DEFAULT_BASE_URL = '/api/dpe';\r\n\r\nexport const DPE_BUILDER_HTTP_CONFIG = new InjectionToken<DpeBuilderHttpConfig>(\r\n 'DPE_BUILDER_HTTP_CONFIG',\r\n { factory: () => ({ baseUrl: DPE_BUILDER_DEFAULT_BASE_URL }) },\r\n);\r\n\r\n@Injectable()\r\nexport class HttpDpeBuilderApi extends DpeBuilderApi {\r\n protected readonly http = inject(HttpClient);\r\n protected readonly config = inject(DPE_BUILDER_HTTP_CONFIG);\r\n\r\n /**\r\n * Deriva da `DpeBuilderHttpConfig.unsupported`: e' il ponte fra la configurazione e la regola\r\n * di `contracts.md` §7. Non e' un doppione della configurazione — e' il campo che\r\n * `core/dpe-capabilities.ts` legge, e leggerlo dal token li' significherebbe portare la\r\n * configurazione HTTP dentro il calcolo delle capacita'.\r\n */\r\n override readonly unsupportedPrimitives: readonly DpeOptionalPrimitive[] =\r\n this.config.unsupported ?? [];\r\n\r\n // --- punti di estensione ---------------------------------------------------\r\n\r\n /**\r\n * Composizione dell'URL. Sovrascriverlo basta a spostare tutte e undici le rotte sotto un altro\r\n * prefisso senza toccare un metodo.\r\n *\r\n * `?? ''` e non un default: `baseUrl: ''` e' una configurazione **valida** e significa «rotte\r\n * relative alla radice», quindi non va rimpiazzata dal default del token. Il percorso porta\r\n * sempre lo slash iniziale, cosi' non nasce un doppio slash.\r\n */\r\n protected url(path: string): string {\r\n return `${this.config.baseUrl ?? ''}${path}`;\r\n }\r\n\r\n /** Codifica di un segmento variabile. Un nome di definizione puo' contenere `/`, spazi, `&`. */\r\n protected segment(value: string): string {\r\n return encodeURIComponent(value);\r\n }\r\n\r\n /**\r\n * La chiave che finisce in un segmento di rotta, **verificata prima di partire**.\r\n *\r\n * Perche' esiste: `encodeURIComponent(undefined)` vale `'undefined'`, e senza questo controllo un\r\n * nome mancante non produceva nessun errore — produceva `PUT /definitions/undefined`, cioe' una\r\n * richiesta sensata per il trasporto e priva di senso per il dominio. La sbaglia il backend\r\n * (`404`, o peggio: sovrascrive una definizione che si chiama davvero cosi'), e chi guarda la rete\r\n * vede una chiave inventata dal client senza sapere da dove arrivi.\r\n *\r\n * Rifiuta **senza emettere la richiesta**: una chiave assente non si ritenta e non riguarda il\r\n * backend. La categoria e' `Backend` e non `MissingService` — il comando c'e' e non va disabilitato\r\n * — ne' `Transport`, che significherebbe «ritenta».\r\n */\r\n protected requireKey(value: string | null | undefined, primitive: string, what: string): string {\r\n if (typeof value !== 'string' || value.trim().length === 0) {\r\n throw new DpeApiError(\r\n 'Backend',\r\n `${primitive}: ${what} manca, e finirebbe nell'URL come \"${String(value)}\". ` +\r\n 'Se il documento arriva dal backend, verifica che la risposta sia in camelCase: ' +\r\n '`Name` invece di `name` non produce nessun errore, produce un campo `undefined`.',\r\n );\r\n }\r\n return value;\r\n }\r\n\r\n /**\r\n * Cio' che avvolge **ogni** chiamata, e l'unico posto in cui si decide se una chiamata parte.\r\n *\r\n * `defer` non e' un vezzo: rende l'`Observable` **freddo** anche per il controllo delle\r\n * primitive non servite, cosi' chi non sottoscrive non valuta nulla e chi si disiscrive annulla\r\n * davvero la richiesta HTTP. E' la proprieta' per cui il contratto §7 e' `Observable` e non\r\n * `Promise` — l'editor la usa per non accumulare validazioni in volo.\r\n *\r\n * Il rigetto per primitiva non servita sta **fuori** dal `catchError`: passandogli dentro, un\r\n * `DpeApiError('MissingService')` verrebbe riclassificato `Transport` da `fail()`, che tratta\r\n * come guasto di rete tutto cio' che non e' una `HttpErrorResponse`.\r\n */\r\n protected request<T>(primitive: DpeOptionalPrimitive | string, call: () => Observable<T>): Observable<T> {\r\n return defer(() => {\r\n if (this.isUnsupported(primitive)) {\r\n return this.missing(primitive);\r\n }\r\n return call().pipe(catchError((error: unknown) => this.fail(error, primitive)));\r\n });\r\n }\r\n\r\n /** Vero se l'ospite ha dichiarato di non servire questa primitiva. */\r\n protected isUnsupported(primitive: string): boolean {\r\n return (this.unsupportedPrimitives as readonly string[]).includes(primitive);\r\n }\r\n\r\n /**\r\n * Rigetto di una primitiva dichiarata non servita, **senza toccare la rete**: sapere in anticipo\r\n * che un servizio non c'e' e chiederglielo comunque e' il difetto che `unsupportedPrimitives`\r\n * esiste per evitare, ed e' il motivo per cui il `501` -> `MissingService` non basta da solo.\r\n */\r\n protected missing(primitive: string): Observable<never> {\r\n return throwError(\r\n () =>\r\n new DpeApiError(\r\n 'MissingService',\r\n `Primitiva non servita da questo ambiente: ${primitive}.`,\r\n ),\r\n );\r\n }\r\n\r\n /**\r\n * `501 Not Implemented` diventa `MissingService`: e' cosi' che un ambiente incompleto degrada\r\n * **un comando** invece di rompere l'editor. Uno `0` (rete assente, CORS) e' `Transport`, come\r\n * qualunque errore che non sia una `HttpErrorResponse`; tutto il resto e' `Backend`.\r\n *\r\n * Il `404` resta deliberatamente `Backend` e non ha un ramo suo: qui non e' «primitiva assente»\r\n * ma «risorsa assente» — una definizione cancellata da altri, un nome che non esiste piu'. Chi\r\n * volesse distinguerlo lo mappi su una categoria propria, non su `MissingService`: disabilitare\r\n * un comando perche' un singolo nome non e' stato trovato sarebbe la reazione sbagliata.\r\n *\r\n * Il messaggio si prende in ordine — corpo dell'errore, messaggio della risposta, ripiego che\r\n * nomina la primitiva — e serve solo all'umano: l'editor guarda la categoria.\r\n */\r\n protected fail(error: unknown, primitive: string): Observable<never> {\r\n /*\r\n * Un `DpeApiError` passa **intatto**: l'ha classificato chi l'ha alzato (la chiave mancante, una\r\n * risposta che non e' una definizione), e ri-tradurlo qui lo farebbe cadere sul ramo «non e' una\r\n * `HttpErrorResponse`», cioe' `Transport` — che significa «ritenta» e sarebbe la reazione\r\n * sbagliata a un contratto rotto.\r\n */\r\n if (DpeApiError.is(error)) {\r\n return throwError(() => error);\r\n }\r\n const response = error instanceof HttpErrorResponse ? error : undefined;\r\n let category: DpeApiErrorCategory = 'Backend';\r\n if (response?.status === 501) {\r\n category = 'MissingService';\r\n } else if (!response || response.status === 0) {\r\n category = 'Transport';\r\n }\r\n const message =\r\n (response?.error as { message?: string } | undefined)?.message ??\r\n response?.message ??\r\n `Chiamata ${primitive} fallita.`;\r\n return throwError(() => new DpeApiError(category, message, error));\r\n }\r\n\r\n // --- verbi ----------------------------------------------------------------\r\n // Sovrascriverne uno cambia il trasporto di tutte le rotte che lo usano: e' il livello a cui\r\n // agisce chi deve aggiungere `withCredentials` o un `responseType` diverso.\r\n\r\n protected httpGet<T>(path: string): Observable<T> {\r\n return this.http.get<T>(this.url(path));\r\n }\r\n\r\n protected httpPost<T>(path: string, body: unknown): Observable<T> {\r\n return this.http.post<T>(this.url(path), body);\r\n }\r\n\r\n protected httpPut<T>(path: string, body: unknown): Observable<T> {\r\n return this.http.put<T>(this.url(path), body);\r\n }\r\n\r\n protected httpDelete<T>(path: string): Observable<T> {\r\n return this.http.delete<T>(this.url(path));\r\n }\r\n\r\n // --- primitive obbligatorie -----------------------------------------------\r\n\r\n /**\r\n * La rotta della validazione. Dal trasloco di `requiredForm` i suoi `schemas` portano **tre**\r\n * fatti per nodo e non due — colonne, compilabilita' e forma richiesta dai siti di espressione —\r\n * e un ospite che serva questa rotta a mano deve valorizzare anche il terzo: e' l'unica strada\r\n * per cui la forma arriva all'editor, e senza di essa un campo formula non dice cosa vuole ne'\r\n * si blocca quando il compilatore non sa produrla.\r\n */\r\n override validate(definition: DpeDefinition): Observable<DpeValidateResult> {\r\n // Il documento va **nudo** nel body: e' la definizione in lavorazione, non un involucro.\r\n return this.request('validate', () =>\r\n this.httpPost<DpeValidateResult>('/validate', definition),\r\n );\r\n }\r\n\r\n override getTables(): Observable<TableSummary[]> {\r\n return this.request('getTables', () => this.httpGet<TableSummary[]>('/tables'));\r\n }\r\n\r\n override getTable(tableName: string): Observable<TableLookupResult> {\r\n return this.request('getTable', () =>\r\n this.httpGet<TableLookupResult>(\r\n `/tables/${this.segment(this.requireKey(tableName, 'getTable', 'il nome della tabella'))}`,\r\n ),\r\n );\r\n }\r\n\r\n // --- primitive opzionali --------------------------------------------------\r\n\r\n /** L'**unica** rotta con involucro: il corpo porta anche i valori dei parametri (§7-bis). */\r\n override compilePreview(\r\n definition: DpeDefinition,\r\n parameterValues?: DpeParameterValue[],\r\n ): Observable<DpeCompileResult> {\r\n return this.request('compilePreview', () =>\r\n this.httpPost<DpeCompileResult>('/compile', { definition, parameterValues }),\r\n );\r\n }\r\n\r\n override load(name: string): Observable<DpeDefinition> {\r\n return this.request('load', () =>\r\n this.httpGet<DpeDefinition>(\r\n `/definitions/${this.segment(this.requireKey(name, 'load', 'il nome della definizione'))}`,\r\n ).pipe(map((payload) => this.requireDefinition(payload))),\r\n );\r\n }\r\n\r\n /**\r\n * La risposta di `load` **e' una definizione**, e qui si verifica invece di sperarlo.\r\n *\r\n * Costa quattro righe e trasforma il difetto peggiore di questo contratto in un errore immediato\r\n * che nomina la causa. Senza, una risposta della forma sbagliata non produce nessun errore:\r\n * produce un editor con il canvas vuoto e un `name` `undefined`, che si manifesta molto piu' tardi\r\n * come un salvataggio verso una chiave inventata. I tre modi in cui succede davvero:\r\n *\r\n * - il backend restituisce **l'involucro della lettura** (`{ definition, diagnostics, hasErrors }`)\r\n * invece del documento: e' l'esito di `IDpeService.Deserialize`, ed e' un errore facile da fare\r\n * scrivendo `Ok(read)` al posto di `Ok(documento)`;\r\n * - la risposta e' in **PascalCase**: `Name` non e' `name`, e in TypeScript non e' un errore;\r\n * - la rotta risponde un **sommario** (`DpeDefinitionSummary`) invece della definizione: si\r\n * somigliano, e il sommario ha `name` ma non `formatVersion` ne' i nodi.\r\n *\r\n * Si controllano le due proprieta' **obbligatorie** del documento e nient'altro: una definizione\r\n * puo' legittimamente non avere nessun nodo, e verificare di piu' vorrebbe dire validare, che e'\r\n * del backend.\r\n */\r\n protected requireDefinition(payload: unknown): DpeDefinition {\r\n const documento = payload as Partial<DpeDefinition> | null | undefined;\r\n const haNome = typeof documento?.name === 'string' && documento.name.trim().length > 0;\r\n const haVersione = typeof documento?.formatVersion === 'number';\r\n if (!haNome || !haVersione) {\r\n throw new DpeApiError(\r\n 'Backend',\r\n \"load: la risposta non e' una definizione (mancano `name` e/o `formatVersion`). \" +\r\n \"Le tre cause reali: il backend restituisce l'involucro di lettura \" +\r\n \"(`{ definition, diagnostics }`) invece del documento; risponde in PascalCase \" +\r\n \"(`Name` non e' `name`); risponde un sommario invece della definizione.\",\r\n payload,\r\n );\r\n }\r\n return payload as DpeDefinition;\r\n }\r\n\r\n /**\r\n * Il nome viene dal **documento** e non da un parametro: e' la chiave, e la rotta e'\r\n * `PUT /definitions/{definition.name}`. Se manca, la chiamata si rifiuta qui — un salvataggio\r\n * senza chiave non e' un salvataggio, e una definizione nuova si crea con `createDefinition`.\r\n */\r\n override save(definition: DpeDefinition): Observable<void> {\r\n return this.request('save', () =>\r\n this.httpPut<void>(\r\n `/definitions/${this.segment(this.requireKey(definition?.name, 'save', 'il nome della definizione'))}`,\r\n definition,\r\n ),\r\n );\r\n }\r\n\r\n /**\r\n * La dichiarazione dell'ambiente (ADR 0017). Questo client la implementa perche' conosce\r\n * l'endpoint, non perche' sappia se l'ospite lo serve — ed e' precisamente la ragione per cui\r\n * l'ADR 0020 ha aggiunto `unsupportedPrimitives`.\r\n *\r\n * Non e' un dettaglio: l'abilitazione della creazione nel catalogo (ADR 0019) dipende da questa\r\n * primitiva, quindi un ambiente che non la serve deve lasciare il comando disabilitato **con il\r\n * motivo**. Da qui i **tre** rami, che sono tre cose diverse e vanno tenute distinte:\r\n *\r\n * - **primitiva non implementata**: il metodo non viene sovrascritto, e il `MissingService` lo\r\n * produce l'identita' stessa del metodo. E' la strada di chi scrive la propria\r\n * implementazione di `DpeBuilderApi`, non di chi eredita questo client;\r\n * - **primitiva implementata ma non servita da questo ambiente**: e' il ramo centrale, quello\r\n * che questo file rende necessario. L'ospite mette `'getEnvironment'` in\r\n * `DpeBuilderHttpConfig.unsupported`, il comando resta disabilitato con il motivo, e **nessuna\r\n * richiesta parte**. Un metodo ereditato non si puo' dis-sovrascrivere: senza questa\r\n * dichiarazione il comando risulterebbe attivo;\r\n * - **primitiva servita che fallisce**: l'endpoint risponde male, e l'errore e' `Transport` o\r\n * `Backend`. Un ambiente rotto, non un ambiente incompleto.\r\n *\r\n * Rispondere `501` da questo endpoint per significare «non lo dichiaro» arriva allo stesso posto\r\n * nel modo sbagliato: costa un giro di rete a ogni apertura dell'editor per comunicare un fatto\r\n * che si conosce a tempo di configurazione.\r\n *\r\n * Cio' che l'editor fa della risposta resta suo: mostra il dialetto in sola lettura e segnala\r\n * senza correggere se differisce da quello della definizione.\r\n */\r\n override getEnvironment(): Observable<DpeEnvironment> {\r\n return this.request('getEnvironment', () => this.httpGet<DpeEnvironment>('/environment'));\r\n }\r\n\r\n /**\r\n * L'**undicesima rotta** (ADR 0036): `POST {base}/formula-catalog/{nodeName}`, corpo il documento\r\n * nudo. Le altre dieci non cambiano.\r\n *\r\n * Perche' un `POST` e non un `GET`: il catalogo dipende dallo **schema a monte** del nodo, che il\r\n * backend sa risolvere solo avendo la definizione, e la definizione aperta nell'editor puo' non\r\n * essere ancora salvata. Un `GET` con il solo nome del nodo risponderebbe sul documento\r\n * archiviato, cioe' su un altro documento.\r\n *\r\n * Perche' il nome del nodo sta nel **percorso** e non nel corpo: un involucro\r\n * `{ definition, nodeName }` e' esattamente l'errore silenzioso del contratto — un backend che\r\n * deserializza una `DpeDefinition` da `{definition: …}` non riconosce nessuna proprieta',\r\n * risponde `200`, e l'editor riceve il catalogo di una definizione vuota. Meglio una rotta che\r\n * non puo' sbagliare che un secondo corpo con involucro.\r\n *\r\n * **La risposta e' `DpeFormulaCatalogResult`, non `DpeFormulaCatalog`** (`contracts.md` §7-bis).\r\n * E' l'unica correzione di questa rotta: consegnava il catalogo nudo, quindi i tre esiti del\r\n * service ne diventavano due e un dialetto non coperto si presentava come un guasto di rete. Il\r\n * client non interpreta niente di cio' che riceve — la tricotomia si legge in\r\n * `dpeFormulaCatalogOutcome`, dove e' pura e verificabile.\r\n *\r\n * **Rottura dichiarata del contratto di rotta:** la risposta non porta piu' `requiredForm`. La\r\n * forma richiesta dai siti di espressione di un nodo sta ora su `DpeNodeSchemaInfo.requiredForm`,\r\n * cioe' negli schemi di `POST {base}/validate`. Un ospite che la serializzi ancora qui non rompe\r\n * niente — il client passa il corpo intero e nessuno la legge — ma resta l'unica copia di un\r\n * fatto che l'editor prende da un'altra parte. Il motivo del trasloco e' che questa rotta\r\n * l'editor non la chiama affatto quando `DpeEnvironment.providesFormulaCatalog` e' falso: la\r\n * forma non arrivava mai, proprio dagli ospiti piu' laconici.\r\n */\r\n override getFormulaCatalog(\r\n definition: DpeDefinition,\r\n nodeName: string,\r\n ): Observable<DpeFormulaCatalogResult> {\r\n return this.request('getFormulaCatalog', () =>\r\n this.httpPost<DpeFormulaCatalogResult>(\r\n `/formula-catalog/${this.segment(\r\n this.requireKey(nodeName, 'getFormulaCatalog', 'il nome del nodo'),\r\n )}`,\r\n definition,\r\n ),\r\n );\r\n }\r\n\r\n // --- catalogo (ADR 0019) --------------------------------------------------\r\n\r\n /** Nessun parametro di interrogazione: il filtro del catalogo e' locale, di proposito. */\r\n override listDefinitions(): Observable<DpeDefinitionSummary[]> {\r\n return this.request('listDefinitions', () =>\r\n this.httpGet<DpeDefinitionSummary[]>('/definitions'),\r\n );\r\n }\r\n\r\n /**\r\n * `POST` e non `PUT`: e' una **creazione**, quindi il backend puo' rifiutare un nome duplicato —\r\n * ed e' l'unica ragione per cui questa primitiva esiste separata da `save`. La risposta e' il\r\n * **sommario**, che chi crea deve adottare: il nome puo' essere stato normalizzato.\r\n */\r\n override createDefinition(definition: DpeDefinition): Observable<DpeDefinitionSummary> {\r\n return this.request('createDefinition', () =>\r\n this.httpPost<DpeDefinitionSummary>('/definitions', definition),\r\n );\r\n }\r\n\r\n override deleteDefinition(name: string): Observable<void> {\r\n return this.request('deleteDefinition', () =>\r\n this.httpDelete<void>(\r\n `/definitions/${this.segment(this.requireKey(name, 'deleteDefinition', 'il nome della definizione'))}`,\r\n ),\r\n );\r\n }\r\n}\r\n\r\n/**\r\n * Registra il client e la sua configurazione: l'integrazione in una riga che l'ADR 0020 chiedeva.\r\n *\r\n * **Non** chiama `provideHttpClient()`, e non e' una dimenticanza: il backend (`withFetch`), gli\r\n * interceptor, l'autenticazione e i cookie sono scelte dell'applicazione ospite, e una libreria\r\n * che le prende al posto suo la costringe a disfarle.\r\n *\r\n * `baseUrl` omesso ricade sul default, `baseUrl: ''` **no**: la stringa vuota e' una\r\n * configurazione valida («rotte relative alla radice») e un `||` la scambierebbe per assente.\r\n */\r\nexport function provideDpeBuilderHttpApi(config?: DpeBuilderHttpConfig): EnvironmentProviders {\r\n return makeEnvironmentProviders([\r\n {\r\n provide: DPE_BUILDER_HTTP_CONFIG,\r\n useValue: {\r\n baseUrl: config?.baseUrl ?? DPE_BUILDER_DEFAULT_BASE_URL,\r\n unsupported: config?.unsupported ?? [],\r\n } satisfies DpeBuilderHttpConfig,\r\n },\r\n { provide: DpeBuilderApi, useClass: HttpDpeBuilderApi },\r\n ]);\r\n}\r\n","/**\r\n * Modello della definizione DPE — traduzione **letterale** dei record C# di\r\n * `docs/domain-model.md`.\r\n *\r\n * Questi tipi sono allineati **a mano** ai record di `Jace.DPE.Model` (ADR 0007: nessun\r\n * generatore di tipi, nessuno JSON Schema). Sono due modelli in due linguaggi e il\r\n * compilatore non dira' mai quando divergono: la fonte di verita' e' `docs/contracts.md` +\r\n * `docs/domain-model.md`, e chi cambia un record C# aggiorna questo file nello stesso\r\n * momento.\r\n *\r\n * Convenzioni, tutte conseguenza del contratto:\r\n *\r\n * - proprieta' in **camelCase**, perche' il JSON prodotto dal backend e' camelCase\r\n * (`domain-model.md`, convenzioni generali);\r\n * - gli enum C# diventano **unioni di stringhe letterali** con i nomi esatti dei valori: un\r\n * `enum` TypeScript non e' assegnabile da una stringa arrivata dalla rete, e un valore\r\n * numerico non e' cio' che il backend serializza;\r\n * - le collezioni nel backend non sono mai nulle, ma il JSON le **omette quando vuote**\r\n * (ADR 0003): qui sono opzionali e chi legge non presume che esistano;\r\n * - `extensionData` esiste su **ogni** record (**I7**): sono proprieta' JSON non riconosciute,\r\n * con valori **opachi**. L'editor le trasporta senza interpretarle.\r\n *\r\n * Nessuna regola di dominio vive qui: obbligatorieta' condizionali, unicita' dei nomi e\r\n * compatibilita' dei tipi sono del backend. Le note `condiz.` nei commenti sono descrittive\r\n * e servono a chi costruira' gli ispettori, non sono validazione.\r\n */\r\n\r\n/** Proprieta' JSON non riconosciute, conservate verbatim (**I7**). Valori opachi. */\r\nexport type DpeExtensionData = Record<string, unknown>;\r\n\r\n/** Base di ogni record del modello: `ExtensionData` sta su tutti, non solo sulla radice. */\r\nexport interface DpeExtensible {\r\n extensionData?: DpeExtensionData;\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// 1. Elementi trasversali\r\n// ---------------------------------------------------------------------------\r\n\r\n/** I sei tipi logici, nessun altro valore mai (ADR 0002). */\r\nexport type LogicalType = 'String' | 'Number' | 'Integer' | 'DateTime' | 'Boolean' | 'Enum';\r\n\r\nexport interface ColumnType extends DpeExtensible {\r\n kind: LogicalType;\r\n /** Condiz.: richiesto per `String` nei campi dichiarati. */\r\n length?: number;\r\n /** Solo `Number`. */\r\n precision?: number;\r\n /** Condiz.: richiesto per `Number` nei campi dichiarati. */\r\n scale?: number;\r\n /** Condiz.: non vuoto quando `kind === 'Enum'`. */\r\n allowedValues?: string[];\r\n}\r\n\r\nexport type SortDirection = 'Ascending' | 'Descending';\r\n\r\nexport interface OrderByField extends DpeExtensible {\r\n /** Alias della colonna a monte. */\r\n columnName: string;\r\n /** Default `Ascending`. */\r\n direction?: SortDirection;\r\n}\r\n\r\nexport type AggregateFunction =\r\n | 'Count'\r\n | 'CountDistinct'\r\n | 'Sum'\r\n | 'Min'\r\n | 'Max'\r\n | 'Avg'\r\n | 'StdDev'\r\n | 'StdDevPop'\r\n | 'Variance'\r\n | 'VariancePop';\r\n\r\n/** Base di tutti i nodi. `name` e' univoco nell'intera definizione (**I1**). */\r\nexport interface DpeNode extends DpeExtensible {\r\n name: string;\r\n label: string;\r\n description?: string;\r\n}\r\n\r\n/**\r\n * I riferimenti a un nodo a monte sono dichiarati **opzionali** anche dove\r\n * `domain-model.md` li marca obbligatori, e non e' un ammorbidimento del contratto: e' cio'\r\n * che il contratto dice. In C# sono `string` con default `null`, l'obbligatorieta' e'\r\n * verificata dalla validazione e \"un modello incompleto e' un fatto normale (l'editor ne\r\n * produce uno a ogni tasto)\". Un nodo appena creato o appena scollegato **non ha** una\r\n * sorgente: rappresentarla con la stringa vuota sarebbe un valore magico che il backend\r\n * legge diversamente da un'assenza.\r\n */\r\n\r\n\r\n// ---------------------------------------------------------------------------\r\n// 2. Radice\r\n// ---------------------------------------------------------------------------\r\n\r\nexport type SqlDialectKind = 'Sqlite' | 'SqlServer' | 'PostgreSql' | 'Oracle';\r\nexport type DefinitionStatus = 'Active' | 'Inactive';\r\nexport type DefinitionRunMode = 'Batch' | 'OnDemand';\r\n\r\nexport interface DpeDefinition extends DpeExtensible {\r\n /** Default `1`, sempre serializzato per primo. */\r\n formatVersion: number;\r\n name: string;\r\n label: string;\r\n description?: string;\r\n /** Il dialetto e' **parte della definizione** (ADR 0009): cambiarlo e' una migrazione. */\r\n dialect: SqlDialectKind;\r\n /** Default `Inactive`. */\r\n status?: DefinitionStatus;\r\n /** Default `Batch`. */\r\n runMode?: DefinitionRunMode;\r\n /** Default `false`. */\r\n isTemplate?: boolean;\r\n parameters?: DpeParameter[];\r\n /** Obbligatoria (>=1) in una definizione valida; assente in una in lavorazione. */\r\n dataSources?: DataSourceNode[];\r\n filters?: FilterNode[];\r\n joins?: JoinNode[];\r\n aggregates?: AggregateNode[];\r\n transforms?: TransformNode[];\r\n appends?: AppendNode[];\r\n hierarchyPaths?: HierarchyPathNode[];\r\n /** Modellati, non compilabili in v1 (`DPE_NODE_NOT_COMPILABLE`). */\r\n forecasts?: ForecastNode[];\r\n /** Modellati, non compilabili in v1. */\r\n customNodes?: CustomNode[];\r\n /** Obbligatoria (>=1) in una definizione valida. */\r\n writebacks?: WritebackNode[];\r\n atomicWritebacks?: AtomicWritebackNode[];\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// 3. Parametri\r\n// ---------------------------------------------------------------------------\r\n\r\nexport type ParameterRole = 'Value' | 'Filter' | 'Expression';\r\n\r\nexport interface DpeParameter extends DpeExtensible {\r\n name: string;\r\n label: string;\r\n description?: string;\r\n role: ParameterRole;\r\n /** Condiz.: richiesto quando `role === 'Value'`. */\r\n valueType?: ColumnType;\r\n /** Testo, interpretato secondo `valueType`, cultura invariante. */\r\n defaultValue?: string;\r\n /** Default `false`; ammesso solo per `role === 'Value'`. */\r\n isMultiValue?: boolean;\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// 4. Nodi sorgente\r\n// ---------------------------------------------------------------------------\r\n\r\nexport type DataSourceKind = 'Table' | 'Csv' | 'ExternalDataset';\r\nexport type CsvDelimiter = 'Comma' | 'Semicolon' | 'Tab' | 'Pipe' | 'Caret' | 'Backquote';\r\n\r\nexport interface CsvSourceOptions extends DpeExtensible {\r\n /** Default `Comma`. */\r\n delimiter?: CsvDelimiter;\r\n /** Default `true`. */\r\n hasHeaderRow?: boolean;\r\n /** Identificatore opaco risolto dall'host. */\r\n location: string;\r\n}\r\n\r\nexport interface DataSourceField extends DpeExtensible {\r\n /** Nome del campo nella tabella/file di origine. */\r\n name: string;\r\n /** Quando assente l'alias **e'** `name`: la normalizzazione e' del backend. */\r\n alias?: string;\r\n /** Condiz.: richiesto per `Csv`/`ExternalDataset`; per `Table` **ignorato**. */\r\n declaredType?: ColumnType;\r\n /** Default `false`. */\r\n isPrimaryKey?: boolean;\r\n}\r\n\r\nexport interface DataSourceNode extends DpeNode {\r\n kind: DataSourceKind;\r\n /** Condiz.: richiesto per `kind === 'Table'`; nome noto al provider di metadati. */\r\n tableName?: string;\r\n /** Obbligatorio (>=1) in un nodo valido. */\r\n fields?: DataSourceField[];\r\n /** Condiz.: richiesto per `kind === 'Csv'`. */\r\n csvOptions?: CsvSourceOptions;\r\n /** Condiz.: richiesto per `kind === 'ExternalDataset'`. */\r\n externalDatasetName?: string;\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// 5. Nodi di trasformazione\r\n// ---------------------------------------------------------------------------\r\n\r\nexport type FilterOperator =\r\n | 'Equals'\r\n | 'NotEquals'\r\n | 'GreaterThan'\r\n | 'GreaterThanOrEqual'\r\n | 'LessThan'\r\n | 'LessThanOrEqual'\r\n | 'StartsWith'\r\n | 'EndsWith'\r\n | 'Contains'\r\n | 'DoesNotContain'\r\n | 'IsNull'\r\n | 'IsNotNull'\r\n | 'In'\r\n | 'NotIn';\r\n\r\nexport interface FilterCriterion extends DpeExtensible {\r\n /** >=1, univoco nel nodo, referenziato da `condition`. */\r\n sequence: number;\r\n /** Alias a monte. */\r\n sourceFieldName: string;\r\n operator: FilterOperator;\r\n /** Condiz.: richiesto salvo `IsNull`/`IsNotNull` e salvo `inputVariable` presente. */\r\n value?: string;\r\n /** Nome di un `DpeParameter` con `role === 'Value'`; esclusivo con `value`. */\r\n inputVariable?: string;\r\n}\r\n\r\nexport interface FilterNode extends DpeNode {\r\n sourceName?: string;\r\n /** Default `false`. */\r\n isDynamic?: boolean;\r\n /** Condiz.: richiesto (>=1) quando `isDynamic` e' falso. */\r\n criteria?: FilterCriterion[];\r\n /**\r\n * Condiz.: richiesto quando `isDynamic` e' falso. Combinazione booleana dei `sequence`\r\n * dei criteri (`1 AND (2 OR 3)`), **non** un'espressione SQL: e' l'unico linguaggio che il\r\n * backend parsifica davvero.\r\n */\r\n condition?: string;\r\n /** Condiz.: richiesto quando `isDynamic`; riferisce un parametro con `role === 'Filter'`. */\r\n filterParameterName?: string;\r\n}\r\n\r\nexport type JoinKind = 'Inner' | 'LeftOuter' | 'RightOuter' | 'FullOuter' | 'Lookup';\r\n\r\nexport interface JoinKey extends DpeExtensible {\r\n primaryFieldName: string;\r\n secondaryFieldName: string;\r\n}\r\n\r\nexport interface JoinResultField extends DpeExtensible {\r\n /** Deve essere `primarySourceName` o `secondarySourceName` del nodo. */\r\n sourceName: string;\r\n sourceFieldName: string;\r\n /** Univoco nel nodo (**I1**). */\r\n alias: string;\r\n}\r\n\r\nexport interface JoinNode extends DpeNode {\r\n primarySourceName?: string;\r\n secondarySourceName?: string;\r\n kind: JoinKind;\r\n /** Obbligatorio (>=1) in un nodo valido. */\r\n keys?: JoinKey[];\r\n /** Lo schema di output e' **esplicito**: solo questi campi. */\r\n fields?: JoinResultField[];\r\n /** Default `false`; significativo solo per `kind === 'Lookup'`. */\r\n allowsMultipleLookupValues?: boolean;\r\n}\r\n\r\nexport interface AggregateField extends DpeExtensible {\r\n /** Alias a monte; assente **solo** per `Count` (conteggio righe). */\r\n sourceFieldName?: string;\r\n function: AggregateFunction;\r\n alias: string;\r\n}\r\n\r\nexport interface AggregateNode extends DpeNode {\r\n sourceName?: string;\r\n /** Alias a monte; vuoto = aggregazione globale su una riga. */\r\n groupBy?: string[];\r\n /** Obbligatorio (>=1) in un nodo valido. */\r\n fields?: AggregateField[];\r\n}\r\n\r\nexport type TransformKind = 'Expression' | 'Slice' | 'ComputeRelative';\r\n\r\nexport interface ExpressionField extends DpeExtensible {\r\n alias: string;\r\n /**\r\n * Una **formula** nel linguaggio del compilatore registrato dall'ospite (ADR 0031): non e'\r\n * SQL, e il frontend non ne conosce la grammatica. Forma `Value` sotto `Expression`, forma\r\n * `WindowFunction` sotto `ComputeRelative`.\r\n */\r\n expression: string;\r\n /**\r\n * **Dichiarato dall'utente e autorevole** (ADR 0033): lo schema resta una funzione pura della\r\n * definizione, quindi il tipo di una colonna calcolata e' questo e non quello inferito dal\r\n * compilatore. Il compilatore lo **verifica** — `DPE_EXPR_TYPE_MISMATCH` /\r\n * `DPE_EXPR_TYPE_WIDENED` / `DPE_EXPR_TYPE_UNVERIFIED` — ma non lo determina.\r\n */\r\n declaredType: ColumnType;\r\n}\r\n\r\nexport interface TransformNode extends DpeNode {\r\n sourceName?: string;\r\n kind: TransformKind;\r\n /** Condiz.: richiesto (>=1) per `Expression`/`ComputeRelative`, vuoto per `Slice`. */\r\n expressionFields?: ExpressionField[];\r\n /** Condiz.: richiesto (>=1) per `Slice`, vuoto altrimenti. */\r\n droppedFields?: string[];\r\n /** Solo `ComputeRelative`. */\r\n partitionBy?: string[];\r\n /** Solo `ComputeRelative`. */\r\n orderBy?: OrderByField[];\r\n}\r\n\r\nexport interface AppendNode extends DpeNode {\r\n /** Obbligatorio (>=2) in un nodo valido. */\r\n sources?: string[];\r\n /** Default `false`. */\r\n allowsDisjointSchema?: boolean;\r\n}\r\n\r\nexport interface HierarchyPathNode extends DpeNode {\r\n sourceName?: string;\r\n /** Chiave del nodo corrente. */\r\n selfFieldName?: string;\r\n /** Riferimento al padre. */\r\n parentFieldName?: string;\r\n /** Alias della colonna di percorso prodotta (`String`). */\r\n pathFieldName?: string;\r\n /** Default `true`. */\r\n includesSelfValue?: boolean;\r\n /** Condiz.: richiesto (>=1) quando `isAggregationRequired`. */\r\n aggregateFields?: AggregateField[];\r\n /** Default `false`. */\r\n isAggregationRequired?: boolean;\r\n rollupPercentageFieldName?: string;\r\n}\r\n\r\nexport type ForecastPeriodType =\r\n | 'Year'\r\n | 'YearQuarter'\r\n | 'YearMonth'\r\n | 'YearWeek'\r\n | 'YearMonthDay'\r\n | 'FiscalYear'\r\n | 'FiscalYearQuarter'\r\n | 'FiscalYearMonth'\r\n | 'FiscalYearWeek';\r\n\r\nexport type ForecastModelType = 'Auto' | 'Additive' | 'Multiplicative';\r\nexport type ForecastAccuracy = 'None' | 'Eighty' | 'NinetyFive';\r\n\r\nexport type DatePart =\r\n | 'Second'\r\n | 'Minute'\r\n | 'Hour'\r\n | 'Day'\r\n | 'Week'\r\n | 'Month'\r\n | 'Quarter'\r\n | 'Year';\r\n\r\nexport interface ForecastAggregationField extends DpeExtensible {\r\n fieldName: string;\r\n function: AggregateFunction;\r\n resultAlias: string;\r\n}\r\n\r\nexport interface ForecastGroupField extends DpeExtensible {\r\n fieldName: string;\r\n /** Condiz.: richiesto quando il campo e' `DateTime`. */\r\n datePart?: DatePart;\r\n}\r\n\r\n/** Modellato per intero, **non compilabile in v1** (`DPE_NODE_NOT_COMPILABLE`). */\r\nexport interface ForecastNode extends DpeNode {\r\n sourceName?: string;\r\n dateFieldName?: string;\r\n periodType?: ForecastPeriodType;\r\n /** Default `1`, ammesso 1..100. */\r\n periodCount?: number;\r\n /** Alias della colonna di inizio periodo prodotta. */\r\n periodStartFieldName?: string;\r\n /** Default `Auto`. */\r\n modelType?: ForecastModelType;\r\n /** `null`/assente = automatica; ammesso 2..24. */\r\n seasonality?: number;\r\n /** Default `None`. */\r\n accuracyPercent?: ForecastAccuracy;\r\n /** Default `false`. */\r\n excludesLastPeriod?: boolean;\r\n /** Obbligatorio (>=1) in un nodo valido. */\r\n aggregationFields?: ForecastAggregationField[];\r\n groupFields?: ForecastGroupField[];\r\n}\r\n\r\n/** Usato dove uno schema si **dichiara** invece di derivarlo. */\r\nexport interface DeclaredField extends DpeExtensible {\r\n name: string;\r\n type: ColumnType;\r\n}\r\n\r\nexport interface CustomNodeParameter extends DpeExtensible {\r\n name: string;\r\n value: string;\r\n}\r\n\r\n/** Modellato per intero, **non compilabile in v1**. */\r\nexport interface CustomNode extends DpeNode {\r\n /** Obbligatorio (>=1) in un nodo valido. */\r\n sources?: string[];\r\n extensionName: string;\r\n extensionNamespace?: string;\r\n parameters?: CustomNodeParameter[];\r\n /**\r\n * Obbligatorio (>=1): senza schema dichiarato la propagazione (**I4**) si interrompe e\r\n * tutto cio' che sta a valle diventa invalidabile.\r\n */\r\n outputFields?: DeclaredField[];\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// 6. Nodi sink\r\n// ---------------------------------------------------------------------------\r\n\r\nexport type WritebackTargetKind = 'Database' | 'Json' | 'Csv';\r\nexport type WritebackOperation = 'Insert' | 'Update' | 'Upsert' | 'Delete';\r\nexport type WritebackFieldRole = 'Value' | 'PrimaryKey' | 'QualifierKey';\r\n\r\n/**\r\n * Granularita' del fallimento che l'host deve rispettare applicando un writeback (ADR 0040).\r\n *\r\n * `PerRecord`: l'host **puo'** applicare le righe indipendentemente e riportare i fallimenti per\r\n * riga. `Atomic`: l'host **deve** applicare l'intero writeback in una transazione sola.\r\n *\r\n * Dichiarata e **non eseguita**: la libreria non apre transazioni e questo valore non cambia di\r\n * una virgola l'SQL generato.\r\n */\r\nexport type WritebackFailureHandling = 'Atomic' | 'PerRecord';\r\n\r\nexport interface WritebackFieldMapping extends DpeExtensible {\r\n /** Condiz.: alias a monte; assente quando `runtimeParameterName` e' presente. */\r\n sourceFieldName?: string;\r\n targetFieldName: string;\r\n /** Il valore viene da un parametro, non dalla riga; esclusivo con `sourceFieldName`. */\r\n runtimeParameterName?: string;\r\n /** Default `Value`. */\r\n role?: WritebackFieldRole;\r\n relationshipName?: string;\r\n /** Condiz.: richiesto quando `relationshipName` e' presente. */\r\n parentName?: string;\r\n /** Default `false`; se `true`, `sourceFieldName` non e' richiesto. */\r\n isAutoGenerated?: boolean;\r\n}\r\n\r\n/**\r\n * Opzioni dell'artefatto JSON (`contracts.md` §2, «Opzioni degli artefatti»). Sono **solo**\r\n * scelte di formato, mai di destinazione (ADR 0010).\r\n */\r\nexport interface JsonArtifactOptions extends DpeExtensible {\r\n /** Default `false`: JSON compatto, array di oggetti. */\r\n indented?: boolean;\r\n}\r\n\r\n/**\r\n * Opzioni dell'artefatto CSV. Il quoting **non e' configurabile** di proposito: si quota\r\n * quando serve e si raddoppiano le virgolette, perche' un quoting opzionale produce file che\r\n * qualcuno non riesce a rileggere.\r\n */\r\nexport interface CsvArtifactOptions extends DpeExtensible {\r\n /** Default `Comma`. */\r\n delimiter?: CsvDelimiter;\r\n /** Default `true`. */\r\n includesHeaderRow?: boolean;\r\n}\r\n\r\nexport interface WritebackNode extends DpeNode {\r\n sourceName?: string;\r\n /** **Determina l'artefatto prodotto**. */\r\n target: WritebackTargetKind;\r\n /** >=1, univoco fra tutti i writeback e gli atomic writeback. */\r\n sequence: number;\r\n /** Condiz.: richiesto per `target === 'Database'`. */\r\n tableName?: string;\r\n /** Condiz.: richiesto per `target === 'Database'`; assente per `Json`/`Csv`. */\r\n operation?: WritebackOperation;\r\n /** Obbligatorio (>=1) in un nodo valido. */\r\n fields?: WritebackFieldMapping[];\r\n /**\r\n * Predicato applicato al risultato: e' il **terzo sito di authoring di formule** (ADR 0031 §1),\r\n * quello che si dimentica, e passa dal compilatore nella forma `Predicate`. Non e' SQL.\r\n */\r\n filterCondition?: string;\r\n /** Condiz.: richiesto per `operation === 'Upsert'`. */\r\n externalIdFieldName?: string;\r\n /** Default `false`. */\r\n onlyChangedRows?: boolean;\r\n /**\r\n * `targetFieldName` della colonna con cui l'host raggruppa le righe per la rilavorazione\r\n * (ADR 0040). Solo `target === 'Database'`. Dichiarato e non eseguito: la libreria non\r\n * raggruppa niente, e senza questo campo l'unico lotto di lavoro possibile e' il writeback\r\n * intero.\r\n */\r\n processingGroupFieldName?: string;\r\n /**\r\n * Solo `target === 'Database'`, e **assente** vuol dire «non dichiarato», che non e' la stessa\r\n * cosa di `PerRecord`: il valore efficace lo calcola il backend e arriva su\r\n * `DpeWritebackStep.failureHandling`.\r\n *\r\n * L'opzionalita' non e' comodita': e' cio' che permette a un salva-dopo-carica di rispedire il\r\n * documento **identico** (**I7**). Se l'editor scrivesse un default qui, una definizione che non\r\n * dichiara niente tornerebbe al backend dichiarando `PerRecord`, e su un membro di gruppo\r\n * atomico quella riga in piu' diventerebbe `DPE_MODEL_CONDITIONAL_FORBIDDEN` su un campo che\r\n * nessuno ha toccato.\r\n */\r\n failureHandling?: WritebackFailureHandling;\r\n /** Solo `target === 'Json'`. */\r\n jsonOptions?: JsonArtifactOptions;\r\n /** Solo `target === 'Csv'`. */\r\n csvOptions?: CsvArtifactOptions;\r\n}\r\n\r\nexport interface AtomicWritebackRelationship extends DpeExtensible {\r\n /** Nome di un `WritebackNode` con `target === 'Database'`. */\r\n parentWritebackName?: string;\r\n parentFieldName?: string;\r\n childWritebackName?: string;\r\n childFieldName?: string;\r\n relationshipName?: string;\r\n /** Ordine dentro il gruppo atomico. */\r\n sequence: number;\r\n}\r\n\r\nexport interface AtomicWritebackNode extends DpeNode {\r\n /** Nello stesso spazio di numerazione dei `WritebackNode`. */\r\n sequence: number;\r\n /** Obbligatorio (>=1) in un nodo valido. */\r\n relationships?: AtomicWritebackRelationship[];\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// Collezioni di nodi: la mappa nome-collezione -> tipo di nodo\r\n// ---------------------------------------------------------------------------\r\n\r\n/**\r\n * Le collezioni di nodi della radice, con il tipo che ciascuna contiene.\r\n *\r\n * Esiste perche' ogni utility che cammina il documento (derivazione degli archi, rinomina,\r\n * catalogo della palette) deve nominare le collezioni **una volta sola**: una collezione\r\n * dimenticata in uno dei tre posti non produce nessun errore, produce un arco che non\r\n * compare o un riferimento che resta rotto dopo una rinomina.\r\n */\r\nexport interface DpeNodeCollectionMap {\r\n dataSources: DataSourceNode;\r\n filters: FilterNode;\r\n joins: JoinNode;\r\n aggregates: AggregateNode;\r\n transforms: TransformNode;\r\n appends: AppendNode;\r\n hierarchyPaths: HierarchyPathNode;\r\n forecasts: ForecastNode;\r\n customNodes: CustomNode;\r\n writebacks: WritebackNode;\r\n atomicWritebacks: AtomicWritebackNode;\r\n}\r\n\r\nexport type DpeNodeCollectionName = keyof DpeNodeCollectionMap;\r\n\r\n/** Qualunque nodo della definizione. */\r\nexport type DpeAnyNode = DpeNodeCollectionMap[DpeNodeCollectionName];\r\n\r\n// L'ordine delle collezioni, scritto una volta sola. E' una tupla `as const` e non un array\r\n// annotato perche' il controllo qui sotto deve vederne i membri uno per uno; `satisfies` rifiuta\r\n// un nome che non sia una collezione.\r\nconst COLLECTION_ORDER = [\r\n 'dataSources',\r\n 'filters',\r\n 'joins',\r\n 'aggregates',\r\n 'transforms',\r\n 'appends',\r\n 'hierarchyPaths',\r\n 'forecasts',\r\n 'customNodes',\r\n 'writebacks',\r\n 'atomicWritebacks',\r\n] as const satisfies readonly DpeNodeCollectionName[];\r\n\r\n/** I membri che compaiono piu' di una volta in una tupla. */\r\ntype DpeRepeatedMembers<L extends readonly unknown[]> = L extends readonly [infer Head, ...infer Rest]\r\n ? (Head extends Rest[number] ? Head : never) | DpeRepeatedMembers<Rest>\r\n : never;\r\n\r\n/**\r\n * `true` se la tupla `L` nomina ogni membro di `U` una volta sola. Altrimenti un oggetto che\r\n * **nomina** i mancanti e i ripetuti: un semplice `false` fermerebbe la build senza dire perche'.\r\n */\r\ntype DpeListCoverage<U, L extends readonly unknown[]> = [\r\n Exclude<U, L[number]>,\r\n DpeRepeatedMembers<L>,\r\n] extends [never, never]\r\n ? true\r\n : { mancanti: Exclude<U, L[number]>; ripetuti: DpeRepeatedMembers<L> };\r\n\r\n// Non si legge mai: esiste per essere compilata. Se l'elenco perde o ripete un membro il tipo\r\n// smette di essere `true` e la build si ferma con «TS2322: Type 'boolean' is not assignable to\r\n// type '{ mancanti: \"atomicWritebacks\"; ripetuti: never; }'», con i nomi scritti dentro.\r\nconst COLLECTION_ORDER_IS_TOTAL: DpeListCoverage<DpeNodeCollectionName, typeof COLLECTION_ORDER> = true;\r\n\r\n/**\r\n * L'elenco dei nomi di collezione in forma di valore, nell'ordine in cui i nodi si leggono\r\n * (sorgenti -> trasformazioni -> sink). `keyof` non produce un array a runtime: questo\r\n * elenco e' l'unico posto in cui vive l'ordine. Lo leggono il cammino del documento\r\n * (`dpe-graph.ts`) e la palette, attraverso `DPE_NODE_CATEGORIES` che ne deriva.\r\n *\r\n * **Totale e senza ripetizioni, e a dirlo e' il compilatore.** Un array non si verifica contro\r\n * un'unione: una collezione aggiunta a `DpeNodeCollectionMap` e dimenticata qui compilerebbe, e\r\n * sparirebbe in silenzio dal cammino del documento (archi che non compaiono, rinomine\r\n * incomplete) e dalla palette. Il controllo `COLLECTION_ORDER_IS_TOTAL` trasforma l'omissione, e\r\n * la ripetizione, in un errore di build che nomina il membro.\r\n */\r\nexport const DPE_NODE_COLLECTIONS: readonly DpeNodeCollectionName[] = COLLECTION_ORDER;\r\n","/**\r\n * Diagnostiche — traduzione letterale di `Jace.DPE.Diagnostics` (`docs/contracts.md` §1).\r\n *\r\n * Tipi allineati **a mano** ai record C# (ADR 0007). L'editor **consuma** queste\r\n * diagnostiche e non ne produce: nessuna regola di dominio vive nel frontend, quindi in\r\n * questo file non ci sono ne' codici inventati ne' messaggi ricomposti. Il `message` arriva\r\n * dal backend, che e' l'unico posto in cui un messaggio viene composto.\r\n */\r\n\r\nexport type DiagnosticSeverity = 'Info' | 'Warning' | 'Error';\r\n\r\nexport interface DiagnosticPathSegment {\r\n /** `\"filters\"`, `\"criteria\"`, `\"fields\"`, ... */\r\n collection?: string;\r\n /** Nome del nodo/alias, se applicabile. */\r\n name?: string;\r\n /** Indice nella collezione, se applicabile. */\r\n index?: number;\r\n}\r\n\r\n/**\r\n * Percorso strutturato del rilievo.\r\n *\r\n * **Due forme, entrambe da gestire** (`contracts.md` §1, limite dichiarato): in\r\n * deserializzazione i nomi dei nodi non sono ancora noti e i segmenti portano solo l'indice\r\n * (`filters[0].sourceName`); dalla validazione portano il nome (`filters/OnlyActive`). Chi\r\n * naviga fino al campo deve accettare tutt'e due, altrimenti le diagnostiche del caricamento\r\n * di un file non sono cliccabili.\r\n */\r\nexport interface DiagnosticPath {\r\n segments?: DiagnosticPathSegment[];\r\n /** Nome della proprieta' in causa, camelCase. */\r\n field?: string;\r\n}\r\n\r\n/**\r\n * `arguments` e' un dizionario di stringhe con **chiavi canoniche** (`contracts.md` §1):\r\n * `node`, `field`, `column`, `alias`, `name`, `table`, `parameter`, `dialect`, `construct`,\r\n * `expected`, `actual`, `value`, `sequence`, `condition`, `token`, `truncated`. L'editor le\r\n * legge per costruire etichette, non per riscrivere il messaggio.\r\n */\r\nexport interface Diagnostic {\r\n /** `DiagnosticCodes.*` del backend. Il frontend non ne dichiara l'elenco: e' del backend. */\r\n code: string;\r\n severity: DiagnosticSeverity;\r\n path?: DiagnosticPath;\r\n arguments?: Record<string, string>;\r\n /** Derivato da `code` + `arguments`, cultura invariante. */\r\n message: string;\r\n}\r\n\r\n/**\r\n * L'unico codice che significa \"hai fatto bene, siamo noi\": nodo valido ma non ancora\r\n * compilabile (`Forecast`, `CustomNode`, costrutto non supportato — **I10**). Va distinto\r\n * visivamente dai `DPE_MODEL_*`, che invece l'utente deve correggere.\r\n *\r\n * E' l'unica costante di codice diagnostico che la libreria dichiara, e non e' una\r\n * duplicazione del dominio: e' la sola distinzione che cambia **come si rende** un rilievo.\r\n */\r\nexport const DPE_NODE_NOT_COMPILABLE = 'DPE_NODE_NOT_COMPILABLE';\r\n\r\n/**\r\n * Il codice che il contratto (`contracts.md` §5 e §7) prescrive di mostrare **in testa** quando il\r\n * catalogo delle formule non e' stato prodotto: nomina la sorgente il cui schema non si risolve,\r\n * mentre le diagnostiche strutturali che lo accompagnano ne dicono la causa.\r\n *\r\n * Seconda e ultima costante di codice dichiarata dalla libreria, per lo stesso criterio della\r\n * prima: non e' un elenco di dominio duplicato, e' l'unica cosa che cambia **come si rende** un\r\n * sacco di rilievi. Senza di essa la riga che l'architetto ha aggiunto apposta — «una riga da\r\n * mostrare in testa» — finirebbe in mezzo alle altre, che e' il rumore indistinguibile che il\r\n * codice esiste per evitare.\r\n */\r\nexport const DPE_CATALOG_UNAVAILABLE = 'DPE_CATALOG_UNAVAILABLE';\r\n\r\n/** Rappresentazione testuale del percorso, nella forma usata dai messaggi del backend. */\r\nexport function diagnosticPathToString(path: DiagnosticPath | undefined): string {\r\n if (!path) {\r\n return '';\r\n }\r\n const segments = path.segments ?? [];\r\n let text = '';\r\n for (const segment of segments) {\r\n if (segment.collection) {\r\n text += text.length > 0 ? `/${segment.collection}` : segment.collection;\r\n }\r\n if (segment.name) {\r\n text += `/${segment.name}`;\r\n } else if (segment.index !== undefined) {\r\n text += `[${segment.index}]`;\r\n }\r\n }\r\n if (path.field) {\r\n text += text.length > 0 ? `.${path.field}` : path.field;\r\n }\r\n return text;\r\n}\r\n","/**\r\n * Tipi di richiesta/risposta del service DPE — traduzione letterale di `docs/contracts.md`\r\n * §2 (provider di metadati), §3 (SQL emesso) e §5 (il service).\r\n *\r\n * Allineati **a mano** ai record C# (ADR 0007). Cio' che non e' trasportabile non c'e':\r\n * `DpeContext` porta i provider dell'host (`ITableMetadataProvider`, `IDataQueryProvider`,\r\n * `IArtifactSink`, `ILogger`, `IClock`) e vive **dentro** il backend, quindi non ha un\r\n * corrispettivo TypeScript. Lo stesso vale per `Execute`: l'editor non esegue, non\r\n * materializza artefatti e non ha bisogno di `DpeExecuteResult`.\r\n */\r\n\r\nimport {\r\n ColumnType,\r\n CsvArtifactOptions,\r\n DefinitionRunMode,\r\n DefinitionStatus,\r\n JsonArtifactOptions,\r\n LogicalType,\r\n SqlDialectKind,\r\n WritebackFailureHandling,\r\n WritebackFieldRole,\r\n WritebackOperation,\r\n WritebackTargetKind,\r\n} from './dpe-definition.model';\r\nimport { Diagnostic } from './dpe-diagnostics.model';\r\n\r\n// ---------------------------------------------------------------------------\r\n// §7 La dichiarazione dell'ambiente (ADR 0017)\r\n// ---------------------------------------------------------------------------\r\n\r\n/**\r\n * Cio' che l'ambiente ospite **dichiara** all'editor. Non e' un tipo del backend C#: `IDpeService`\r\n * non cambia (ADR 0017), e l'host lo compone con cio' che gia' sa della propria installazione.\r\n *\r\n * Nasce con un solo campo e si estende **per aggiunta**: e' il posto previsto per i fatti\r\n * dell'ambiente, non un contenitore da riempire in anticipo.\r\n */\r\nexport interface DpeEnvironment {\r\n /** Il dialetto del database di destinazione. Dichiarato, mai scelto dall'utente dell'editor. */\r\n readonly dialect: SqlDialectKind;\r\n /**\r\n * Nome del linguaggio in cui si scrivono le espressioni, dal compilatore di formule registrato\r\n * dall'ospite (ADR 0031/0036).\r\n *\r\n * Serve **all'etichetta del campo**, e per questo sta qui e non nel catalogo: il catalogo puo'\r\n * legittimamente non arrivare — il compilatore non descrive il proprio linguaggio\r\n * (`ProvidesCatalog == false`), oppure il dialetto della definizione non e' fra quelli che\r\n * copre e allora il catalogo e' nullo — mentre il nome del linguaggio si sa sempre. Un editor\r\n * che leggesse il nome dal catalogo resterebbe muto proprio nei casi in cui l'utente ha piu'\r\n * bisogno di sapere in che lingua sta scrivendo.\r\n *\r\n * Assente = l'editor non nomina nessun linguaggio. Non nomina il **dialetto** al suo posto: il\r\n * dialetto non c'entra piu' con cio' che si scrive in quel campo (ADR 0031).\r\n */\r\n readonly expressionLanguage?: string;\r\n /**\r\n * Il compilatore descrive il proprio linguaggio. Falso o assente: **non chiamare**\r\n * `getFormulaCatalog`.\r\n *\r\n * Si interroga **prima** di chiamare, per la stessa ragione per cui la disponibilita' di un\r\n * comando si sa prima del clic (`contracts.md` §7): una chiamata che si sa inutile costa al\r\n * backend una passata completa di grafo e di metadati per restituire un catalogo vuoto.\r\n */\r\n readonly providesFormulaCatalog?: boolean;\r\n /**\r\n * Le **forme** che il compilatore registrato sa produrre (`Capabilities.SupportedForms`, ADR\r\n * 0031). Incrociata con `DpeNodeSchemaInfo.requiredForm` — che e' per **nodo** e arriva con la\r\n * validazione — permette di **disabilitare** un campo formula che non potra' mai essere\r\n * compilato.\r\n *\r\n * Le due meta' stanno in due posti perche' hanno due sorgenti e due vite: la forma richiesta\r\n * viene dalla **definizione**, le forme supportate dal **compilatore registrato**, cioe'\r\n * dall'installazione, come `expressionLanguage` e `providesFormulaCatalog` qui accanto.\r\n * L'editor le incrocia e non deduce nessuna delle due.\r\n *\r\n * **Assente (o `null`) significa: non si disabilita niente.** Non e' un ripiego prudente, e' la\r\n * politica: non sapere quali forme il compilatore copra non autorizza a indovinare, e un campo\r\n * spento per ignoranza e' peggio di un campo acceso che il backend rifiutera' dicendo perche'.\r\n * Un elenco **vuoto** e' un'altra cosa — e' una dichiarazione, e dice «nessuna forma».\r\n *\r\n * Opzionale **e** nullabile per la regola dei tipi di rispecchiamento: descrivono cio' che\r\n * arriva **sul filo**, e l'ospite di riferimento ignora i nulli come politica globale, quindi la\r\n * proprieta' puo' non comparire affatto.\r\n */\r\n readonly supportedForms?: readonly DpeExpressionForm[] | null;\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// §7 Catalogo del linguaggio di formule (ADR 0031, 0036)\r\n// ---------------------------------------------------------------------------\r\n\r\n/**\r\n * La **forma** che i siti di espressione di un nodo pretendono (ADR 0031 §1).\r\n *\r\n * Attraversa il confine su `DpeNodeSchemaInfo.requiredForm` per una ragione sola: dedurla dal tipo\r\n * di nodo — «una trasformazione `Expression` vuole un valore, il filtro di un writeback vuole un\r\n * predicato» — sarebbe una **regola di dominio in TypeScript**, che e' vietata. La sa il backend, e\r\n * la dice.\r\n *\r\n * Il tipo sta in questa sezione e non accanto agli schemi perche' e' la **stessa** enumerazione che\r\n * `DpeEnvironment.supportedForms` dichiara: sono i due operandi dell'incrocio, e un'enumerazione\r\n * dichiarata due volte diverge.\r\n */\r\nexport type DpeExpressionForm = 'Value' | 'Predicate' | 'WindowFunction';\r\n\r\n/**\r\n * L'**involucro** di `getFormulaCatalog`, rispecchiamento di `DpeFormulaCatalogResult`\r\n * (`contracts.md` §7, normativo). La rotta consegna questo, non il catalogo nudo.\r\n *\r\n * Il motivo e' misurato, non di principio: il service distingue **tre** esiti e il filo ne\r\n * consegnava **due** — o un catalogo o un errore di trasporto — quindi *dialetto non coperto* e\r\n * *backend rotto* arrivavano all'editor identici, e il degrado era silenzioso.\r\n *\r\n * **Il discriminante e' `diagnostics`, non `catalog`:** diagnostiche non vuote significa\r\n * fallimento, e si mostrano; altrimenti un catalogo c'e', e la tavolozza si mostra se ha\r\n * contenuto. In particolare un compilatore che non descrive il proprio linguaggio consegna il\r\n * catalogo **presente e vuoto**, con `languageName` valorizzato e zero diagnostiche: chi ramasse\r\n * su `catalog == null` non entrerebbe **mai** in quel caso.\r\n *\r\n * La derivazione dei tre esiti sta in `dpeFormulaCatalogOutcome`, una funzione pura, e non in un\r\n * `if` dentro un componente.\r\n */\r\nexport interface DpeFormulaCatalogResult {\r\n /**\r\n * Il catalogo. **Assente o `null` significa una cosa sola: non e' stato prodotto** — non\r\n * «prodotto vuoto», che e' un esito diverso e si riconosce dagli elenchi vuoti.\r\n *\r\n * Opzionale **e** nullabile insieme, e non e' pignoleria: un tipo di rispecchiamento descrive\r\n * cio' che arriva **sul filo**, non cio' che il record C# dichiara, e ospiti diversi\r\n * serializzano diversamente. L'ospite di riferimento ignora i nulli come politica globale,\r\n * quindi la proprieta' non compare affatto e arriva `undefined` mentre un tipo con il solo\r\n * `| null` prometterebbe altro. `check:modelli` confronta i **nomi** dei campi e questa classe\r\n * di divergenza non la vede.\r\n */\r\n readonly catalog?: DpeFormulaCatalog | null;\r\n /**\r\n * Le diagnostiche del service. Mai nulla; **vuota** quando il compilatore semplicemente non\r\n * descrive il proprio linguaggio, che non e' un errore.\r\n *\r\n * `DPE_CATALOG_UNAVAILABLE` e' la riga da mostrare **in testa**: nomina la sorgente il cui\r\n * schema non si risolve, e le strutturali che la seguono ne dicono la causa. Non si filtrano\r\n * per pertinenza — l'errore che spiega lo schema puo' stare a piu' salti di distanza — quindi\r\n * diventano dettaglio espandibile invece di rumore indistinguibile.\r\n */\r\n readonly diagnostics: readonly Diagnostic[];\r\n readonly hasErrors: boolean;\r\n /*\r\n * `requiredForm` **non sta piu' qui**: e' su `DpeNodeSchemaInfo`, dentro il risultato di\r\n * `validate`. Stava qui per parentela di comodita' — il catalogo si chiede per nodo — ma le due\r\n * cose rispondono a due domande diverse: questo involucro dice «cio' che il compilatore offre», e\r\n * arriva **solo quando il compilatore risponde**; la forma e' «cio' che la libreria ha derivato\r\n * su quel nodo», e la libreria la sa senza chiedere niente a nessuno.\r\n *\r\n * La differenza si misurava: un ospite con `providesFormulaCatalog` falso non fa chiamare questa\r\n * rotta all'editor, quindi la forma non arrivava **mai** — e l'incrocio con\r\n * `DpeEnvironment.supportedForms` non poteva scattare proprio dove il compilatore e' piu'\r\n * laconico. Non resta anche qui: due copie dello stesso fatto divergono (ADR 0018).\r\n */\r\n}\r\n\r\n/**\r\n * Rispecchiamento sul filo di `Jace.DPE.Expressions.DpeFormulaCatalog`: cosa il linguaggio di\r\n * formule dell'ospite mette a disposizione **nel punto** in cui si sta scrivendo.\r\n *\r\n * **Non e' una regola di dominio e non ne diventa una** (`contracts.md` §7). L'editor lo usa per\r\n * *mostrare* cosa esiste; non lo usa per validare cio' che l'utente scrive. La grammatica e' del\r\n * compilatore, il frontend non la conosce, e nessuna validazione di formula viene scritta in\r\n * TypeScript — per nessun motivo, nemmeno «solo per il caso banale»: una regola duplicata diverge,\r\n * e quando diverge l'editor mente all'utente.\r\n *\r\n * Un catalogo **vuoto e' legittimo** (ADR 0036 §5) e non e' un errore: significa che il\r\n * compilatore non descrive il proprio linguaggio, e l'editor mostra un campo di testo semplice.\r\n */\r\nexport interface DpeFormulaCatalog {\r\n /**\r\n * Nome del linguaggio secondo il compilatore. **Non** e' la fonte dell'etichetta del campo:\r\n * quella e' `DpeEnvironment.expressionLanguage`, che arriva anche quando il catalogo non arriva.\r\n */\r\n readonly languageName: string;\r\n readonly functions: readonly DpeFormulaFunction[];\r\n readonly operators: readonly DpeFormulaOperator[];\r\n readonly symbols: readonly DpeFormulaSymbol[];\r\n}\r\n\r\nexport interface DpeFormulaFunction {\r\n /** Gruppo con cui l'elenco raggruppa la voce. */\r\n readonly group: string;\r\n readonly name: string;\r\n readonly description?: string;\r\n /**\r\n * Assente quando il tipo di ritorno **dipende dagli argomenti** e un elenco statico non puo'\r\n * dichiararlo (ADR 0036 §4). Assente significa «non si sa qui», non `String`.\r\n */\r\n readonly returnType?: ColumnType;\r\n readonly parameters: readonly DpeFormulaFunctionParameter[];\r\n}\r\n\r\nexport interface DpeFormulaFunctionParameter {\r\n readonly name: string;\r\n readonly description?: string;\r\n readonly type?: ColumnType;\r\n readonly isOptional?: boolean;\r\n /**\r\n * Il parametro appartiene a un gruppo che **si ripete** (funzione variadica). Un solo campo al\r\n * posto della coppia gruppo/ordine del sistema di origine: il matching delle firme e' lavoro del\r\n * compilatore, a un elenco per l'utente serve sapere che il gruppo si ripete.\r\n */\r\n readonly isRepeating?: boolean;\r\n}\r\n\r\nexport interface DpeFormulaOperator {\r\n readonly group: string;\r\n readonly symbol: string;\r\n readonly description?: string;\r\n}\r\n\r\n/**\r\n * Provenienza di un simbolo. Le tre sono strutturalmente diverse e l'elenco le raggruppa: una\r\n * colonna a monte, un parametro di runtime della definizione e una costante del linguaggio non si\r\n * correggono nello stesso modo quando il nome e' sbagliato.\r\n */\r\nexport type DpeFormulaSymbolKind = 'Column' | 'Parameter' | 'Constant';\r\n\r\nexport interface DpeFormulaSymbol {\r\n readonly name: string;\r\n readonly description?: string;\r\n /** `null`/assente quando il compilatore non sa mapparlo sui sei tipi logici: una mancanza\r\n * dichiarata e' meglio di una bugia. */\r\n readonly type?: ColumnType;\r\n readonly kind: DpeFormulaSymbolKind;\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// §7 Catalogo delle definizioni (ADR 0019)\r\n// ---------------------------------------------------------------------------\r\n\r\n/**\r\n * Riga di catalogo: identita' e fatti che l'ospite conosce **senza** caricare la definizione.\r\n *\r\n * Distinto da `DpeDefinition` per costruzione, e non per comodita': elencare non deve costare N\r\n * caricamenti completi. Non ha un corrispettivo in `IDpeService` e non deve averlo — il service\r\n * valida, compila ed esegue; dove le definizioni vivano e' dell'ospite (ADR 0007), che non ha\r\n * ne' orologio ne' archiviazione lato libreria.\r\n *\r\n * I quattro campi in coda sono facoltativi in senso pieno: assenti, la riga **non li mostra e\r\n * non li stima**. Contare i nodi lato client vorrebbe dire caricare tutte le definizioni per\r\n * disegnare un elenco, e dedurre `updatedAt` e' impossibile.\r\n */\r\nexport interface DpeDefinitionSummary {\r\n /** La chiave. Non esiste un id distinto dal nome (ADR 0019): rinominare e' cambiare chiave. */\r\n name: string;\r\n label: string;\r\n description?: string;\r\n dialect: SqlDialectKind;\r\n status?: DefinitionStatus;\r\n runMode?: DefinitionRunMode;\r\n isTemplate?: boolean;\r\n /** ISO 8601. Lo scrive l'ospite: la libreria non ha orologio. Assente = non mostrato. */\r\n updatedAt?: string;\r\n updatedBy?: string;\r\n /** Numero di nodi. Lo conta l'ospite, **mai** il client. Assente = non mostrato. */\r\n nodeCount?: number;\r\n /**\r\n * Esito dell'ultima validazione **noto all'ospite**: riportato, non ricalcolato. Puo' essere\r\n * vecchio, quindi la riga lo mostra come stato noto e non come verita' — il frontend non\r\n * valida nulla, e dedurlo qui sarebbe una validazione fatta dal client per interposta persona.\r\n */\r\n hasErrors?: boolean;\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// §2 Provider di metadati\r\n// ---------------------------------------------------------------------------\r\n\r\n/**\r\n * Tre esiti, **non due** (ADR 0004): \"dichiarata\", \"dichiarata senza campi\" e\r\n * \"sconosciuta\" sono situazioni diverse e l'editor le mostra diverse. Chi implementa\r\n * l'interfaccia non appiattisce `Unknown` in una lista di campi vuota.\r\n */\r\nexport type TableLookupOutcome = 'Declared' | 'DeclaredWithoutFields' | 'Unknown';\r\n\r\nexport interface TableSummary {\r\n name: string;\r\n description?: string;\r\n}\r\n\r\nexport interface FieldRelationship {\r\n targetTableName: string;\r\n targetFieldName: string;\r\n}\r\n\r\nexport interface FieldDescriptor {\r\n name: string;\r\n description?: string;\r\n type: ColumnType;\r\n isKey: boolean;\r\n isNullable: boolean;\r\n /** Assente quando il campo non e' una relazione. */\r\n relationship?: FieldRelationship;\r\n}\r\n\r\nexport interface TableDescriptor {\r\n name: string;\r\n description?: string;\r\n fields?: FieldDescriptor[];\r\n}\r\n\r\nexport interface TableLookupResult {\r\n outcome: TableLookupOutcome;\r\n /** Presente solo con `outcome === 'Declared'`. */\r\n table?: TableDescriptor;\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// §3 SQL emesso\r\n// ---------------------------------------------------------------------------\r\n\r\nexport interface SqlParameterValue {\r\n /** Senza prefisso di dialetto: `\"p0\"`, `\"param_StartDate\"`. */\r\n name: string;\r\n type: LogicalType;\r\n /** `null` ammesso. Valore opaco per l'editor, che lo mostra e non lo interpreta. */\r\n value: unknown;\r\n}\r\n\r\nexport interface SqlStatement {\r\n /** Porta i marcatori nella forma del dialetto (`@p0`, `:p0`, `$1`). */\r\n text: string;\r\n parameters?: SqlParameterValue[];\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// §5 Il service: Validate\r\n// ---------------------------------------------------------------------------\r\n\r\nexport interface DpeColumnInfo {\r\n name: string;\r\n type: ColumnType;\r\n /**\r\n * Informativa (**I4**): serve alla vista dello schema, non blocca la compilazione. Il\r\n * frontend non la calcola — arriva da qui.\r\n */\r\n isNullable: boolean;\r\n}\r\n\r\n/**\r\n * **Cio' che la libreria ha derivato su un nodo**: schema di uscita, compilabilita' e forma pretesa\r\n * dai suoi siti di espressione. Arriva con **ogni** validazione, e da ADR 0041 con **una voce per\r\n * ogni nodo della definizione, sempre** — anche per i nodi che il grafo non ha potuto ordinare.\r\n *\r\n * La presenza di una voce dice «questo nodo esiste, ed ecco cio' che la libreria ha potuto derivare\r\n * su di esso»; **non** dice «il suo schema e' noto». Quello lo dice `hasSchema`, e solo quello.\r\n */\r\nexport interface DpeNodeSchemaInfo {\r\n nodeName: string;\r\n /**\r\n * Colonne di uscita. **Vuota non significa \"nessuna colonna\"** finche' non si e' letto\r\n * `hasSchema`: e' vuota anche quando lo schema e' ignoto. Contarle senza guardare `hasSchema`\r\n * afferma un fatto che il backend non ha affermato — ed e' il difetto misurato sul canvas prima\r\n * dell'ADR 0041.\r\n *\r\n * **Obbligatoria**, come nel contratto (`contracts.md` §5): in C# e' `IReadOnlyList` mai nulla e\r\n * arriva come `[]` quando e' vuota, quindi un ospite che omette la collezione vuota e' fuori\r\n * contratto e non un caso da assecondare nel tipo. Dichiararla opzionale costringeva a un `?? []`\r\n * a ogni lettura, e quel `?? []` e' **indistinguibile fra «vuota» e «non arrivata»** — cioe' la\r\n * stessa ambiguita' che l'ADR 0041 ha tolto da `Columns`, riaperta un livello piu' in basso e in\r\n * TypeScript. La regola generale (`contracts.md` §7): cio' che puo' mancare si dichiara opzionale\r\n * **e** nullabile insieme, e una collezione non e' fra quelle cose.\r\n */\r\n columns: DpeColumnInfo[];\r\n /**\r\n * Il discriminante fra «schema derivato» e «schema ignoto» (ADR 0041).\r\n *\r\n * - `true` = `columns` e' un **fatto**. Vuota vuol dire davvero zero colonne — stato\r\n * raggiungibile, e gia' diagnosticato con `DPE_SCHEMA_EMPTY`.\r\n * - `false` = schema **ignoto**: la derivazione non e' arrivata a una risposta (fallita qui o a\r\n * monte, oppure nodo dentro un ciclo). `columns` e' vuota e **non significa niente**.\r\n *\r\n * **Assente = `false`.** Un ospite che serializza omettendo i falsi non manda affatto la\r\n * proprieta', e qui l'omissione coincide con la risposta prudente: e' il motivo per cui il campo\r\n * si chiama `hasSchema` e non `isSchemaUnknown`. Chi legge confronta quindi con `=== true`, mai\r\n * per veridicita', e non lo deduce da `columns.length` — dedurlo sarebbe rimettere in TypeScript\r\n * la regola che questo campo esiste per portare.\r\n */\r\n hasSchema?: boolean;\r\n /**\r\n * `false` = nodo **valido ma non compilabile** (**I10**): e' un limite nostro, non un\r\n * errore dell'utente, e va reso diverso da un nodo invalido.\r\n */\r\n isCompilable: boolean;\r\n /**\r\n * La forma pretesa dai siti di espressione di questo nodo, oppure assente/`null` se il nodo non\r\n * ne ha — che e' uno stato normale, non una mancanza.\r\n *\r\n * Sta qui e non sul risultato del catalogo perche' e' una cosa che la **libreria deriva** dal\r\n * tipo del nodo (ADR 0031 §1), non una cosa che il **compilatore offre**: l'editor la riceve\r\n * sempre, anche quando il catalogo non lo chiede affatto. Dedurla lato editor sarebbe una regola\r\n * di dominio in TypeScript, che e' vietata; incrociarla con `DpeEnvironment.supportedForms`\r\n * permette invece di **disabilitare** un campo che il compilatore registrato non compilera' mai.\r\n *\r\n * Opzionale **e** nullabile: un tipo di rispecchiamento descrive cio' che arriva **sul filo**, e\r\n * l'ospite di riferimento omette i nulli come politica globale — quindi la proprieta' puo' non\r\n * comparire affatto.\r\n */\r\n requiredForm?: DpeExpressionForm | null;\r\n}\r\n\r\nexport interface DpeWritebackStep {\r\n writebackName: string;\r\n sequence: number;\r\n target: WritebackTargetKind;\r\n /** Assente per i target `Json`/`Csv`. */\r\n operation?: WritebackOperation;\r\n tableName?: string;\r\n /**\r\n * Campo di corrispondenza dell'`Upsert`; assente per ogni altra operazione. E' un\r\n * `targetFieldName`: si ritrova in `targetMapping` per risalire alla colonna del result set\r\n * (ADR 0016).\r\n */\r\n externalIdFieldName?: string;\r\n /** Presente se il writeback appartiene a un gruppo atomico. */\r\n atomicGroupName?: string;\r\n /**\r\n * Il valore **efficace** per `target === 'Database'` (ADR 0040): membro di un gruppo atomico ->\r\n * `Atomic`, altrimenti il valore dichiarato sul nodo, altrimenti `PerRecord`.\r\n *\r\n * **Assente per `Json`/`Csv`**, e segue la regola comune a tutti i campi dello `Step` che\r\n * istruiscono chi scrive — `operation`, `tableName`, `externalIdFieldName`,\r\n * `processingGroupFieldName`, `tenantColumnName`: sono assenti quando l'ospite non scrive.\r\n * L'assenza dice una cosa precisa, «questo non lo scrivi tu», e dirla e' meglio di un\r\n * `PerRecord` che su un artefatto sarebbe pure falso: un file si scrive per intero o per niente.\r\n *\r\n * Non e' il campo del nodo con un altro nome. `WritebackNode.failureHandling` e' cio' che\r\n * l'autore ha scritto, questo e' cio' che l'host deve fare: ricavarlo qui incrociando\r\n * `atomicGroupName` con il valore dichiarato e con il default vorrebbe dire reimplementare tre\r\n * regole di dominio nell'editor, che e' esattamente cio' che l'editor non fa. Se il dato serve,\r\n * arriva di qui.\r\n */\r\n failureHandling?: WritebackFailureHandling;\r\n /**\r\n * Dichiarato, **non implementato dalla libreria**: il change tracking e' dell'host\r\n * (ADR 0011) e non influenza l'SQL generato. Va mostrato come una dichiarazione che l'host\r\n * deve onorare, non come una cosa che il motore fa.\r\n */\r\n onlyChangedRows?: boolean;\r\n}\r\n\r\nexport interface DpeValidateResult {\r\n hasErrors: boolean;\r\n /** Esaustive (**I6**): l'elenco completo, non il primo rilievo. */\r\n diagnostics?: Diagnostic[];\r\n /** Ordine topologico. */\r\n schemas?: DpeNodeSchemaInfo[];\r\n executionOrder?: string[];\r\n writebackSequence?: DpeWritebackStep[];\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// §5 Il service: Compile\r\n// ---------------------------------------------------------------------------\r\n\r\n/**\r\n * Valore di un parametro di runtime per una compilazione (`DpeParameterValue`).\r\n *\r\n * `value` e' **opaco**: il tipo dichiarato del parametro lo conosce il backend, che lo\r\n * interpreta secondo `ColumnType` in cultura invariante. L'editor lo trasporta e lo mostra,\r\n * non lo converte.\r\n */\r\nexport interface DpeParameterValue {\r\n name: string;\r\n value: unknown;\r\n}\r\n\r\n/**\r\n * Cio' che serve all'host per **eseguire** la scrittura che la libreria non esegue\r\n * (`contracts.md` §2, «`DpeFieldMappingInfo`»): senza, l'host riceve un result set e non sa\r\n * quale colonna va in quale campo ne' quale sia la chiave.\r\n */\r\nexport interface DpeFieldMappingInfo {\r\n /** Nome della colonna nel result set dello `SqlStatement`. */\r\n resultColumnName?: string;\r\n targetFieldName: string;\r\n role: WritebackFieldRole;\r\n /** Presente: il valore viene da un parametro, non dalla riga. */\r\n runtimeParameterName?: string;\r\n /** La destinazione genera il valore: non e' nel result set. */\r\n isAutoGenerated?: boolean;\r\n relationshipName?: string;\r\n parentName?: string;\r\n}\r\n\r\nexport interface DpeCompiledWriteback {\r\n writebackName: string;\r\n sequence: number;\r\n /**\r\n * L'intento di scrittura di questo writeback: operazione, tabella, chiave di corrispondenza,\r\n * gruppo atomico, target. **Mai assente** (`contracts.md` §5), ed e' la **stessa istanza** che\r\n * compare in `DpeCompileResult.writebackSequence` (ADR 0018).\r\n *\r\n * Sta qui e non duplicato campo per campo su questo tipo: senza di esso il client non ha modo di\r\n * sapere *cosa* fa uno statement — mostrerebbe l'SQL di un `Upsert` e di una `Delete` allo stesso\r\n * modo — e l'unica alternativa sarebbe incrociare `writebackSequence` per nome, cioe' ricostruire\r\n * a mano un collegamento che il backend manda gia' fatto.\r\n */\r\n step: DpeWritebackStep;\r\n statement: SqlStatement;\r\n resultColumns?: DpeColumnInfo[];\r\n /** Vedi {@link DpeFieldMappingInfo}: forma provvisoria. */\r\n targetMapping?: DpeFieldMappingInfo[];\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// §5 Il piano di artefatto (ADR 0029)\r\n// ---------------------------------------------------------------------------\r\n\r\n/**\r\n * Il formato dell'artefatto prodotto. Due soli valori: `Database` non e' un artefatto e qui e'\r\n * impossibile per costruzione — per questo il piano non riusa `WritebackTargetKind`.\r\n */\r\nexport type ArtifactKind = 'Json' | 'Csv';\r\n\r\n/**\r\n * Una colonna dell'artefatto, con i **due nomi** che l'ADR 0015 tiene distinti: `resultName`\r\n * lega la colonna del result set, `headerName` e' l'unico che viene scritto nel file.\r\n * Mostrarli come uno solo nasconderebbe proprio la divergenza che quell'ADR ha chiuso.\r\n */\r\nexport interface DpeArtifactColumn {\r\n /** Alias SQL: serve a legare la colonna del result set. */\r\n resultName: string;\r\n /** Nome authored: e' quello che finisce nel file (ADR 0015). */\r\n headerName: string;\r\n type: LogicalType;\r\n}\r\n\r\n/**\r\n * Il payload auto-sufficiente con cui l'ospite puo' **schedulare** la scrittura di un artefatto\r\n * (ADR 0029): lo accoda, lo rilegge dopo un riavvio e lo riporta a `WriteArtifact`.\r\n *\r\n * L'editor non lo esegue e non lo materializza — `DpeBuilderApi` non acquista nessuna primitiva\r\n * per questo — ma lo riceve dentro `DpeCompileResult.fileWritebacks`, e l'anteprima ne mostra\r\n * l'SQL di lettura.\r\n *\r\n * `tenant` del record C# **non** e' qui, e non per dimenticanza: l'ambito tenant non attraversa\r\n * il confine verso l'editor, per la stessa ragione per cui `getTable` reda la colonna di tenant\r\n * (ADR 0027). Un backend che lo serializzi comunque non e' un errore: il client lo ignora.\r\n */\r\nexport interface DpeArtifactPlan {\r\n /**\r\n * Timbro del formato del payload. Il piano puo' tornare indietro **vecchio** da una coda, e un\r\n * valore ignoto viene rifiutato dal backend invece che tollerato.\r\n */\r\n formatVersion: number;\r\n definitionName: string;\r\n /** E' anche il nome passato al sink dell'ospite: il piano non porta un `artifactName` distinto. */\r\n writebackName: string;\r\n sequence: number;\r\n kind: ArtifactKind;\r\n /** Lo legge l'ospite per scegliere la connessione, non la libreria. */\r\n dialect: SqlDialectKind;\r\n /** SQL di **lettura**: nessun artefatto e' stato prodotto. */\r\n statement: SqlStatement;\r\n columns?: DpeArtifactColumn[];\r\n /** Presenti solo per il `kind` corrispondente. */\r\n jsonOptions?: JsonArtifactOptions;\r\n csvOptions?: CsvArtifactOptions;\r\n}\r\n\r\nexport interface DpeCompileResult {\r\n hasErrors: boolean;\r\n diagnostics?: Diagnostic[];\r\n databaseWritebacks?: DpeCompiledWriteback[];\r\n /**\r\n * ADR 0029: **piani di artefatto**, non writeback compilati. Portano gia' kind, opzioni e\r\n * intestazioni authored, cioe' tutto cio' che serve a scrivere il file; il `step` non c'e'\r\n * perche' per un file sarebbe fatto di soli campi nulli. L'SQL di lettura non e' ancora stato\r\n * eseguito.\r\n */\r\n fileWritebacks?: DpeArtifactPlan[];\r\n writebackSequence?: DpeWritebackStep[];\r\n}\r\n","/**\r\n * Quali primitive **opzionali** l'ospite implementa davvero — `contracts.md` §7, normativo.\r\n *\r\n * La rilevazione e' per **identita' del metodo**: se il metodo dell'istanza e' ancora quello del\r\n * prototipo di `DpeBuilderApi`, l'ospite non lo ha sovrascritto e la primitiva non c'e'. E'\r\n * esatta, non richiede una primitiva di interrogazione e non aggiunge un giro di rete.\r\n *\r\n * Perche' un solo posto: la disponibilita' va saputa **prima del clic**. Lo schema precedente —\r\n * partire da «disponibile» e scoprire l'assenza dal primo `MissingService` — mostra un comando\r\n * attivo che fallisce alla pressione, cioe' insegna a non fidarsi dei bottoni, e per giunta\r\n * costringe ogni consumatore a ripetere lo stesso trattamento dell'errore.\r\n *\r\n * Regola per chi implementa un host (normativa, `contracts.md` §7): **chi non supporta una\r\n * primitiva non la sovrascrive**. Una sovrascrittura che lancia subito non equivale all'assenza —\r\n * l'editor la considera disponibile e mostra il comando attivo.\r\n *\r\n * **Dichiarazione sottrattiva (ADR 0020).** Da quando la libreria spedisce un client HTTP che\r\n * implementa tutte le primitive, l'identita' da sola non basta: chi eredita quel client\r\n * non puo' dis-sovrascrivere un metodo. Disponibile diventa quindi «**sovrascritta** e **non\r\n * elencata** in `unsupportedPrimitives`». La seconda meta' e' solo sottrattiva — non rende\r\n * disponibile cio' che non e' implementato — e resta calcolata in questo file e in nessun altro.\r\n */\r\n\r\nimport { DpeBuilderApi } from '../api/dpe-builder-api';\r\n\r\n/** Le primitive che un ambiente ospite puo' legittimamente non offrire. */\r\nexport type DpeOptionalPrimitive =\r\n | 'compilePreview'\r\n | 'load'\r\n | 'save'\r\n | 'getEnvironment'\r\n | 'getFormulaCatalog'\r\n | 'listDefinitions'\r\n | 'createDefinition'\r\n | 'deleteDefinition';\r\n\r\ntype MethodBag = Record<string, unknown>;\r\n\r\nexport function dpeIsPrimitiveAvailable(\r\n api: DpeBuilderApi,\r\n primitive: DpeOptionalPrimitive,\r\n): boolean {\r\n // Prima la dichiarazione dell'ospite: se il servizio non c'e', non conta chi ha scritto il\r\n // metodo. E' l'unico modo per un ospite di spegnere una primitiva che ha **ereditato**.\r\n if (api.unsupportedPrimitives?.includes(primitive)) {\r\n return false;\r\n }\r\n const base = DpeBuilderApi.prototype as unknown as MethodBag;\r\n const implementation = (api as unknown as MethodBag)[primitive];\r\n return typeof implementation === 'function' && implementation !== base[primitive];\r\n}\r\n\r\n/** L'insieme delle primitive disponibili: comodo per fotografarlo una volta alla costruzione. */\r\nexport interface DpeCapabilities {\r\n readonly compilePreview: boolean;\r\n readonly load: boolean;\r\n readonly save: boolean;\r\n readonly getEnvironment: boolean;\r\n /**\r\n * Il catalogo del linguaggio di formule (ADR 0036). **Condizione necessaria e non sufficiente**\r\n * per chiederlo: l'altra meta' e' `DpeEnvironment.providesFormulaCatalog`, che e' un fatto del\r\n * compilatore e non del trasporto. Le due stanno separate perche' sono due cose diverse — «il\r\n * backend non serve questa rotta» e «il compilatore non descrive il proprio linguaggio» — e\r\n * mescolarle renderebbe impossibile distinguere un ambiente incompleto da un compilatore\r\n * laconico.\r\n */\r\n readonly getFormulaCatalog: boolean;\r\n readonly listDefinitions: boolean;\r\n readonly createDefinition: boolean;\r\n readonly deleteDefinition: boolean;\r\n}\r\n\r\nexport function dpeReadCapabilities(api: DpeBuilderApi): DpeCapabilities {\r\n return {\r\n compilePreview: dpeIsPrimitiveAvailable(api, 'compilePreview'),\r\n load: dpeIsPrimitiveAvailable(api, 'load'),\r\n save: dpeIsPrimitiveAvailable(api, 'save'),\r\n getEnvironment: dpeIsPrimitiveAvailable(api, 'getEnvironment'),\r\n getFormulaCatalog: dpeIsPrimitiveAvailable(api, 'getFormulaCatalog'),\r\n listDefinitions: dpeIsPrimitiveAvailable(api, 'listDefinitions'),\r\n createDefinition: dpeIsPrimitiveAvailable(api, 'createDefinition'),\r\n deleteDefinition: dpeIsPrimitiveAvailable(api, 'deleteDefinition'),\r\n };\r\n}\r\n\r\n/**\r\n * Disponibilita' di un comando, con **il motivo** quando manca: un bottone disabilitato che non\r\n * dice perche' manda a cercare un guasto che non c'e'.\r\n */\r\nexport interface DpeCommandAvailability {\r\n readonly isAvailable: boolean;\r\n /** Presente **solo** quando il comando non e' disponibile. Va nel `title` del bottone. */\r\n readonly reason?: string;\r\n}\r\n\r\n/**\r\n * Il comando di **creazione** e' l'unico che dipende da **due** primitive\r\n * (`contracts.md` §7, normativo): `createDefinition` per scrivere, e `getEnvironment` perche' il\r\n * dialetto della definizione nuova viene dall'ambiente (ADR 0017).\r\n *\r\n * Perche' senza `getEnvironment` si disabilita invece di ripiegare su un dialetto qualsiasi: il\r\n * dialetto decide con quali capacita' la definizione verra' compilata, e quali costrutti il\r\n * compilatore di formule copre (ADR 0031 §5: un dialetto fuori da `SupportedDialects` non compila\r\n * **nessuna** espressione), e chiederlo all'utente e' proprio la scelta che l'ADR 0017 ha tolto. Un\r\n * comando disabilitato che dice perche' e' meglio di entrambe; il rimedio per l'ospite e' una\r\n * riga, implementare `getEnvironment`.\r\n *\r\n * Sta qui e non dentro un componente per la stessa ragione delle altre: la disponibilita' si sa\r\n * **prima del clic**, e in un solo posto.\r\n */\r\nexport function dpeCreateCommandAvailability(\r\n capabilities: DpeCapabilities,\r\n): DpeCommandAvailability {\r\n const missing: string[] = [];\r\n if (!capabilities.createDefinition) {\r\n missing.push('createDefinition');\r\n }\r\n if (!capabilities.getEnvironment) {\r\n missing.push('getEnvironment');\r\n }\r\n if (missing.length === 0) {\r\n return { isAvailable: true };\r\n }\r\n return {\r\n isAvailable: false,\r\n reason:\r\n missing.length === 2\r\n ? 'Creazione non disponibile: questo ambiente non implementa createDefinition ne’ getEnvironment. ' +\r\n 'Senza la dichiarazione dell’ambiente il dialetto non si sceglie per ripiego (ADR 0017).'\r\n : missing[0] === 'createDefinition'\r\n ? 'Creazione non disponibile: questo ambiente non implementa createDefinition.'\r\n : 'Creazione non disponibile: questo ambiente non dichiara l’ambiente (getEnvironment), ' +\r\n 'quindi il dialetto della definizione nuova non e’ noto e non si sceglie per ripiego (ADR 0017).',\r\n };\r\n}\r\n","/**\r\n * Il grafo della definizione: **un solo posto** costruisce gli id di nodi, porte e archi, e\r\n * **un solo elenco** descrive dove vivono i riferimenti fra nodi.\r\n *\r\n * Perche' sta tutto qui. Un id di porta costruito in due modi diversi (canvas da un lato,\r\n * derivazione degli archi dall'altro) fa sparire un arco **senza nessun errore**: e' il\r\n * difetto piu' difficile da trovare in un editor a canvas. Allo stesso modo, un riferimento\r\n * dimenticato nell'elenco degli slot fa restare incoerente il documento dopo una rinomina,\r\n * anche li' in silenzio.\r\n *\r\n * Cosa **non** sta qui: nessuna regola di dominio. Questo file dice *dove* stanno i nomi dei\r\n * nodi a monte, non se un riferimento e' valido, se il grafo ha cicli (**I2**), se un nodo e'\r\n * a valle (**I3**) o quali colonne produca (**I4**). Quelle risposte arrivano da\r\n * `DpeBuilderApi.validate`.\r\n */\r\n\r\nimport {\r\n AggregateNode,\r\n AppendNode,\r\n AtomicWritebackNode,\r\n CustomNode,\r\n DataSourceNode,\r\n DPE_NODE_COLLECTIONS,\r\n DpeAnyNode,\r\n DpeDefinition,\r\n DpeNodeCollectionMap,\r\n DpeNodeCollectionName,\r\n FilterNode,\r\n ForecastNode,\r\n HierarchyPathNode,\r\n JoinNode,\r\n TransformNode,\r\n WritebackNode,\r\n} from '../model/dpe-definition.model';\r\n\r\n// ---------------------------------------------------------------------------\r\n// Id — l'unico posto che li costruisce\r\n// ---------------------------------------------------------------------------\r\n\r\n/**\r\n * Separatore dei componenti di un id. Un nome di nodo non lo contiene mai: i nomi sono\r\n * identificatori del modello e il backend li usa come identificatori SQL.\r\n */\r\nconst SEP = '::';\r\n\r\nexport function dpeNodeId(nodeName: string): string {\r\n return `node${SEP}${nodeName}`;\r\n}\r\n\r\n/**\r\n * Porta di ingresso. Una porta **per slot** (e per indice, sugli slot a lista): un `JoinNode`\r\n * ha due ingressi distinti e attaccare l'arco alla porta sbagliata scambia primaria e\r\n * secondaria, che e' un join diverso.\r\n */\r\nexport function dpeInputPortId(nodeName: string, slot: string, index?: number): string {\r\n const suffix = index === undefined ? '' : `${SEP}${index}`;\r\n return `in${SEP}${nodeName}${SEP}${slot}${suffix}`;\r\n}\r\n\r\n/** Porta di uscita: una sola per nodo, lo schema prodotto e' uno. */\r\nexport function dpeOutputPortId(nodeName: string): string {\r\n return `out${SEP}${nodeName}`;\r\n}\r\n\r\n/**\r\n * Id dell'arco: identifica la **destinazione** (nodo, slot, indice), non la coppia di nodi.\r\n * Due archi possono unire gli stessi due nodi su slot diversi — la primaria e la secondaria\r\n * di un self-join — e con un id per coppia uno dei due sparirebbe.\r\n */\r\nexport function dpeEdgeId(targetNodeName: string, slot: string, index?: number): string {\r\n const suffix = index === undefined ? '' : `${SEP}${index}`;\r\n return `edge${SEP}${targetNodeName}${SEP}${slot}${suffix}`;\r\n}\r\n\r\n/**\r\n * Gli inversi. Stanno **accanto** ai costruttori e non nel canvas: la libreria di canvas\r\n * restituisce gli id nei suoi eventi, e chi li legge deve tornare al nome del nodo con la\r\n * stessa convenzione con cui li ha scritti. Separarli e' il modo con cui un giorno cambierebbe\r\n * uno solo dei due.\r\n */\r\nexport function dpeParseNodeId(id: string): string | undefined {\r\n return id.startsWith(`node${SEP}`) ? id.slice(4 + SEP.length) : undefined;\r\n}\r\n\r\nexport function dpeParseOutputPortId(id: string): string | undefined {\r\n return id.startsWith(`out${SEP}`) ? id.slice(3 + SEP.length) : undefined;\r\n}\r\n\r\nexport function dpeParseInputPortId(\r\n id: string,\r\n): { nodeName: string; slot: string; index?: number } | undefined {\r\n if (!id.startsWith(`in${SEP}`)) {\r\n return undefined;\r\n }\r\n const parts = id.slice(2 + SEP.length).split(SEP);\r\n if (parts.length < 2) {\r\n return undefined;\r\n }\r\n const [nodeName, slot, rawIndex] = parts;\r\n const index = rawIndex === undefined ? undefined : Number(rawIndex);\r\n return {\r\n nodeName,\r\n slot,\r\n index: index === undefined || Number.isNaN(index) ? undefined : index,\r\n };\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// Slot dei riferimenti — l'unico elenco\r\n// ---------------------------------------------------------------------------\r\n\r\n/**\r\n * Uno slot e' un punto del nodo che contiene il nome di **un altro nodo**.\r\n *\r\n * `read` e `write` sono **posizionali e di lunghezza stabile**: `write(node, read(node))` non\r\n * cambia il nodo. E' la proprieta' che permette di implementare la rinomina come\r\n * `write(node, read(node).map(rinomina))` — cioe' con un solo cammino di codice, invece di\r\n * due elenchi (uno per leggere gli archi, uno per rinominare) che al primo campo aggiunto\r\n * diventano due elenchi diversi.\r\n */\r\nexport interface DpeSourceSlot<TNode> {\r\n readonly slot: string;\r\n /**\r\n * `true` quando lo slot e' un **arco del DAG** (il flusso dei dati). `false` per i\r\n * riferimenti che sono altro: i `sourceName` dei `JoinResultField` ripetono le sorgenti del\r\n * join, e le relazioni di un gruppo atomico dichiarano una transazione dell'host, non un\r\n * flusso di dati (l'ordinamento dei sink e' esplicito e separato dal DAG).\r\n */\r\n readonly isEdge: boolean;\r\n read(node: TNode): readonly (string | undefined)[];\r\n write(node: TNode, names: readonly (string | undefined)[]): TNode;\r\n}\r\n\r\nfunction single<TNode, TKey extends keyof TNode>(\r\n slot: string,\r\n key: TKey,\r\n isEdge = true,\r\n): DpeSourceSlot<TNode> {\r\n return {\r\n slot,\r\n isEdge,\r\n read: (node) => [node[key] as unknown as string | undefined],\r\n // Il cast copre solo la chiave calcolata: TypeScript non sa che `key` di `TNode` porta una\r\n // stringa, e la mappa dei tipi non e' esprimibile senza questo passaggio.\r\n write: (node, names) => ({ ...node, [key]: names[0] }) as unknown as TNode,\r\n };\r\n}\r\n\r\nfunction list<TNode, TKey extends keyof TNode>(\r\n slot: string,\r\n key: TKey,\r\n isEdge = true,\r\n): DpeSourceSlot<TNode> {\r\n return {\r\n slot,\r\n isEdge,\r\n read: (node) => ((node[key] as unknown as string[] | undefined) ?? []).slice(),\r\n // Uno slot a lista non si accorcia in scrittura: `write` riscrive i nomi al loro posto.\r\n // Aggiungere o togliere una sorgente e' una mutazione dello store, non una riscrittura.\r\n write: (node, names) =>\r\n ({ ...node, [key]: names.map((name) => name ?? '') }) as unknown as TNode,\r\n };\r\n}\r\n\r\nconst JOIN_RESULT_FIELDS_SLOT: DpeSourceSlot<JoinNode> = {\r\n slot: 'resultFields',\r\n isEdge: false,\r\n read: (node) => (node.fields ?? []).map((field) => field.sourceName),\r\n write: (node, names) =>\r\n node.fields === undefined\r\n ? node\r\n : {\r\n ...node,\r\n fields: node.fields.map((field, index) => ({ ...field, sourceName: names[index] ?? '' })),\r\n },\r\n};\r\n\r\n/**\r\n * Le relazioni di un gruppo atomico portano **due** nomi di writeback ciascuna, padre e\r\n * figlio. Sono letti e riscritti in coppia e in ordine: se questo slot mancasse, rinominare\r\n * un writeback lascerebbe il gruppo atomico che punta a un nome che non esiste piu' — senza\r\n * nessun errore lato editor, e con un rifiuto del backend che parla di un nodo che l'utente\r\n * non ha toccato.\r\n */\r\nconst ATOMIC_RELATIONSHIPS_SLOT: DpeSourceSlot<AtomicWritebackNode> = {\r\n slot: 'relationships',\r\n isEdge: false,\r\n read: (node) =>\r\n (node.relationships ?? []).flatMap((relationship) => [\r\n relationship.parentWritebackName,\r\n relationship.childWritebackName,\r\n ]),\r\n write: (node, names) =>\r\n node.relationships === undefined\r\n ? node\r\n : {\r\n ...node,\r\n relationships: node.relationships.map((relationship, index) => ({\r\n ...relationship,\r\n parentWritebackName: names[index * 2],\r\n childWritebackName: names[index * 2 + 1],\r\n })),\r\n },\r\n};\r\n\r\n/**\r\n * Gli slot di ogni collezione. Aggiungere un nodo significa aggiungere una riga qui.\r\n *\r\n * **Che ci sia una riga per ogni collezione lo garantisce il compilatore**, non un test: il tipo\r\n * e' mappato sull'unione `DpeNodeCollectionName`, quindi una collezione aggiunta al modello e\r\n * dimenticata qui ferma la build («Property '...' is missing»), e `DPE_NODE_COLLECTIONS`, con cui\r\n * il documento si cammina, e' a sua volta controllato totale. Il test che lo verificava a runtime\r\n * non poteva fallire, e per questo e' stato tolto: aggiungeva la convinzione di una copertura, non\r\n * la copertura.\r\n */\r\nexport const DPE_SOURCE_SLOTS: {\r\n readonly [K in DpeNodeCollectionName]: readonly DpeSourceSlot<DpeNodeCollectionMap[K]>[];\r\n} = {\r\n // Le sorgenti sono i punti d'ingresso del grafo: non riferiscono nessun nodo a monte.\r\n dataSources: [] as readonly DpeSourceSlot<DataSourceNode>[],\r\n filters: [single<FilterNode, 'sourceName'>('source', 'sourceName')],\r\n joins: [\r\n single<JoinNode, 'primarySourceName'>('primary', 'primarySourceName'),\r\n single<JoinNode, 'secondarySourceName'>('secondary', 'secondarySourceName'),\r\n JOIN_RESULT_FIELDS_SLOT,\r\n ],\r\n aggregates: [single<AggregateNode, 'sourceName'>('source', 'sourceName')],\r\n transforms: [single<TransformNode, 'sourceName'>('source', 'sourceName')],\r\n appends: [list<AppendNode, 'sources'>('sources', 'sources')],\r\n hierarchyPaths: [single<HierarchyPathNode, 'sourceName'>('source', 'sourceName')],\r\n forecasts: [single<ForecastNode, 'sourceName'>('source', 'sourceName')],\r\n customNodes: [list<CustomNode, 'sources'>('sources', 'sources')],\r\n writebacks: [single<WritebackNode, 'sourceName'>('source', 'sourceName')],\r\n atomicWritebacks: [ATOMIC_RELATIONSHIPS_SLOT],\r\n};\r\n\r\n// ---------------------------------------------------------------------------\r\n// Lettura del documento\r\n// ---------------------------------------------------------------------------\r\n\r\n/** Un nodo con la collezione da cui viene: senza, non si sa quali slot leggere. */\r\nexport interface DpeLocatedNode<K extends DpeNodeCollectionName = DpeNodeCollectionName> {\r\n collection: K;\r\n index: number;\r\n node: DpeNodeCollectionMap[K];\r\n}\r\n\r\n/** Tutti i nodi, nell'ordine delle collezioni (sorgenti -> trasformazioni -> sink). */\r\nexport function dpeAllNodes(definition: DpeDefinition): DpeLocatedNode[] {\r\n const result: DpeLocatedNode[] = [];\r\n for (const collection of DPE_NODE_COLLECTIONS) {\r\n const nodes = (definition[collection] ?? []) as DpeAnyNode[];\r\n nodes.forEach((node, index) => {\r\n result.push({ collection, index, node } as DpeLocatedNode);\r\n });\r\n }\r\n return result;\r\n}\r\n\r\nexport function dpeFindNode(definition: DpeDefinition, nodeName: string): DpeLocatedNode | undefined {\r\n return dpeAllNodes(definition).find((located) => located.node.name === nodeName);\r\n}\r\n\r\nexport interface DpeSourceReference {\r\n /** Nodo che contiene il riferimento (la destinazione dell'arco). */\r\n targetNodeName: string;\r\n collection: DpeNodeCollectionName;\r\n slot: string;\r\n /** Presente solo sugli slot a lista. */\r\n index?: number;\r\n isEdge: boolean;\r\n /** Nome riferito; assente quando lo slot e' vuoto (nodo non collegato). */\r\n sourceName?: string;\r\n}\r\n\r\n/** I riferimenti di un singolo nodo, in ordine di slot. */\r\nexport function dpeNodeSourceReferences(located: DpeLocatedNode): DpeSourceReference[] {\r\n const slots = DPE_SOURCE_SLOTS[located.collection] as readonly DpeSourceSlot<DpeAnyNode>[];\r\n const references: DpeSourceReference[] = [];\r\n for (const slot of slots) {\r\n const names = slot.read(located.node);\r\n const isPositional = names.length > 1 || slotIsPositional(slot);\r\n names.forEach((name, index) => {\r\n references.push({\r\n targetNodeName: located.node.name,\r\n collection: located.collection,\r\n slot: slot.slot,\r\n index: isPositional ? index : undefined,\r\n isEdge: slot.isEdge,\r\n sourceName: name === '' ? undefined : name,\r\n });\r\n });\r\n }\r\n return references;\r\n}\r\n\r\n/**\r\n * Uno slot a lista porta l'indice **anche** quando contiene un solo elemento: l'id della\r\n * porta e dell'arco non deve cambiare forma quando l'utente aggiunge la seconda sorgente a\r\n * un `AppendNode`, altrimenti la selezione dell'arco salta e la posizione della porta cambia\r\n * sotto il puntatore.\r\n */\r\nfunction slotIsPositional(slot: DpeSourceSlot<DpeAnyNode>): boolean {\r\n return slot.slot === 'sources' || slot.slot === 'resultFields' || slot.slot === 'relationships';\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// Archi\r\n// ---------------------------------------------------------------------------\r\n\r\nexport interface DpeGraphNode {\r\n id: string;\r\n name: string;\r\n collection: DpeNodeCollectionName;\r\n outputPortId: string;\r\n}\r\n\r\nexport interface DpeGraphEdge {\r\n id: string;\r\n /** Nome del nodo a monte, come scritto nel documento. */\r\n fromNodeName: string;\r\n toNodeName: string;\r\n slot: string;\r\n index?: number;\r\n fromPortId: string;\r\n toPortId: string;\r\n /**\r\n * `false` quando il nome a monte non corrisponde a nessun nodo della definizione. L'arco\r\n * **si mostra comunque**: la topologia mostrata e' quella reale, e un riferimento rotto va\r\n * visto, non nascosto. La diagnostica che lo qualifica (`DPE_REF_UNKNOWN_NODE`) arriva dal\r\n * backend.\r\n */\r\n isResolved: boolean;\r\n}\r\n\r\nexport interface DpeGraph {\r\n nodes: DpeGraphNode[];\r\n edges: DpeGraphEdge[];\r\n}\r\n\r\n/**\r\n * Deriva nodi e archi dalla definizione, per riferimento di nome. Funzione pura: nessuna\r\n * lettura di stato, nessuna dipendenza da Angular, testabile senza DOM.\r\n */\r\nexport function dpeBuildGraph(definition: DpeDefinition): DpeGraph {\r\n const located = dpeAllNodes(definition);\r\n const known = new Set(located.map((entry) => entry.node.name));\r\n\r\n const nodes: DpeGraphNode[] = located.map((entry) => ({\r\n id: dpeNodeId(entry.node.name),\r\n name: entry.node.name,\r\n collection: entry.collection,\r\n outputPortId: dpeOutputPortId(entry.node.name),\r\n }));\r\n\r\n const edges: DpeGraphEdge[] = [];\r\n for (const entry of located) {\r\n for (const reference of dpeNodeSourceReferences(entry)) {\r\n if (!reference.isEdge || reference.sourceName === undefined) {\r\n continue;\r\n }\r\n edges.push({\r\n id: dpeEdgeId(reference.targetNodeName, reference.slot, reference.index),\r\n fromNodeName: reference.sourceName,\r\n toNodeName: reference.targetNodeName,\r\n slot: reference.slot,\r\n index: reference.index,\r\n fromPortId: dpeOutputPortId(reference.sourceName),\r\n toPortId: dpeInputPortId(reference.targetNodeName, reference.slot, reference.index),\r\n isResolved: known.has(reference.sourceName),\r\n });\r\n }\r\n }\r\n\r\n return { nodes, edges };\r\n}\r\n\r\n/** Le porte di ingresso di un nodo, nell'ordine in cui vanno disegnate. */\r\nexport interface DpeInputPort {\r\n id: string;\r\n slot: string;\r\n index?: number;\r\n /** Nome collegato, assente se la porta e' libera. */\r\n sourceName?: string;\r\n}\r\n\r\nexport function dpeInputPorts(located: DpeLocatedNode): DpeInputPort[] {\r\n return dpeNodeSourceReferences(located)\r\n .filter((reference) => reference.isEdge)\r\n .map((reference) => ({\r\n id: dpeInputPortId(reference.targetNodeName, reference.slot, reference.index),\r\n slot: reference.slot,\r\n index: reference.index,\r\n sourceName: reference.sourceName,\r\n }));\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// Riscrittura dei riferimenti\r\n// ---------------------------------------------------------------------------\r\n\r\n/**\r\n * Riscrive un riferimento in **uno** slot di **un** nodo. E' il mattone di `connect` e\r\n * `disconnect` dello store: passando `undefined` la porta torna libera.\r\n */\r\nexport function dpeWriteSourceReference<K extends DpeNodeCollectionName>(\r\n collection: K,\r\n node: DpeNodeCollectionMap[K],\r\n slotName: string,\r\n index: number | undefined,\r\n sourceName: string | undefined,\r\n): DpeNodeCollectionMap[K] {\r\n const slots = DPE_SOURCE_SLOTS[collection] as readonly DpeSourceSlot<DpeNodeCollectionMap[K]>[];\r\n const slot = slots.find((candidate) => candidate.slot === slotName);\r\n if (!slot) {\r\n return node;\r\n }\r\n const names = slot.read(node).slice();\r\n const position = index ?? 0;\r\n // Uno slot a lista puo' non avere ancora la posizione richiesta: la si apre, altrimenti\r\n // collegare la seconda sorgente di un Append non avrebbe nessun effetto e nessun errore.\r\n while (names.length <= position) {\r\n names.push(undefined);\r\n }\r\n names[position] = sourceName;\r\n return slot.write(node, names);\r\n}\r\n\r\n/**\r\n * **Rinominare un nodo e' un refactoring globale, non locale** (**I5**).\r\n *\r\n * Questa e' l'unica funzione che rinomina: aggiorna il `name` del nodo e **ogni** riferimento\r\n * a valle, passando dall'unico elenco di slot — `sourceName`, `sources`,\r\n * `primarySourceName`/`secondarySourceName`, i `sourceName` dei `JoinResultField` e i nomi\r\n * dentro i gruppi atomici. Se una collezione di riferimenti sfuggisse, il documento\r\n * diventerebbe silenziosamente incoerente: e' il difetto che questa funzione esiste per non\r\n * avere.\r\n *\r\n * Cosa **non** fa, di proposito: non rinomina gli **alias di colonna** (sono un altro spazio\r\n * di nomi) e non decide se il nuovo nome sia lecito o gia' usato — l'unicita' e' **I1**, la\r\n * verifica il backend.\r\n *\r\n * Il confronto e' **esatto**, non case-insensitive: `domain-model.md` dichiara il confronto\r\n * `OrdinalIgnoreCase` per gli **alias di colonna** e non dice nulla sui nomi dei nodi.\r\n * Rinominare anche le occorrenze con capitalizzazione diversa significherebbe, se il backend\r\n * risolvesse i nomi in modo esatto, riscrivere il riferimento a un nodo **diverso**.\r\n */\r\nexport function dpeRenameNode(\r\n definition: DpeDefinition,\r\n oldName: string,\r\n newName: string,\r\n): DpeDefinition {\r\n if (oldName === newName) {\r\n return definition;\r\n }\r\n\r\n const next: DpeDefinition = { ...definition };\r\n\r\n for (const collection of DPE_NODE_COLLECTIONS) {\r\n const nodes = definition[collection] as DpeAnyNode[] | undefined;\r\n if (!nodes || nodes.length === 0) {\r\n continue;\r\n }\r\n const slots = DPE_SOURCE_SLOTS[collection] as readonly DpeSourceSlot<DpeAnyNode>[];\r\n const rewritten = nodes.map((node) => {\r\n let result = node;\r\n for (const slot of slots) {\r\n const names = slot.read(result);\r\n if (!names.some((name) => name === oldName)) {\r\n continue;\r\n }\r\n result = slot.write(\r\n result,\r\n names.map((name) => (name === oldName ? newName : name)),\r\n );\r\n }\r\n if (result.name === oldName) {\r\n result = { ...result, name: newName };\r\n }\r\n return result;\r\n });\r\n // L'assegnazione per nome di collezione non e' esprimibile in modo tipato senza un cast:\r\n // la mappa collezione -> tipo di nodo esiste, ma TypeScript non correla la chiave con il\r\n // valore dentro un ciclo. Il cast e' confinato a questa riga.\r\n (next as unknown as Record<string, unknown>)[collection] = rewritten;\r\n }\r\n\r\n return next;\r\n}\r\n\r\n/** Quante occorrenze toccherebbe una rinomina: serve a dirlo prima, non dopo. */\r\nexport function dpeCountReferences(definition: DpeDefinition, nodeName: string): number {\r\n let count = 0;\r\n for (const located of dpeAllNodes(definition)) {\r\n for (const reference of dpeNodeSourceReferences(located)) {\r\n if (reference.sourceName === nodeName) {\r\n count += 1;\r\n }\r\n }\r\n }\r\n return count;\r\n}\r\n","/**\r\n * Il controllo di un valore che arriva da fuori e che si vuole usare come nome di collezione.\r\n *\r\n * **Non sta in `public-api.ts`, di proposito.** `public-api.ts` riesporta per intero sia\r\n * `model/dpe-definition.model` sia `core/dpe-node-catalog`, quindi un `export` in uno dei due\r\n * sarebbe API pubblica: questo file esiste per poter servire due siti della libreria senza\r\n * diventarlo. Chi lo volesse esportare deve deciderlo come API, non trovarcelo.\r\n *\r\n * I due confini che lo usano convertono un dato che il tipo non garantisce: il `fData` di un\r\n * rilascio sul canvas (`unknown` per @foblex/flow) e il primo segmento del percorso di una\r\n * diagnostica, che arriva dal backend e nomina anche array della definizione che collezioni di\r\n * nodi non sono — `parameters` su tutti. Dopo questo controllo `dpeNodeCategory` puo' trattare\r\n * un valore fuori dall'unione come un errore di programmazione, e lanciare.\r\n */\r\n\r\nimport { DPE_NODE_COLLECTIONS, DpeNodeCollectionName } from '../model/dpe-definition.model';\r\n\r\n/** Lo stesso elenco del modello, non una copia: una collezione nuova vale qui senza toccarlo. */\r\nconst NODE_COLLECTIONS: ReadonlySet<unknown> = new Set<unknown>(DPE_NODE_COLLECTIONS);\r\n\r\n/** Vero solo per un membro di `DPE_NODE_COLLECTIONS`, a confronto esatto (maiuscole comprese). */\r\nexport function dpeIsNodeCollectionName(value: unknown): value is DpeNodeCollectionName {\r\n return NODE_COLLECTIONS.has(value);\r\n}\r\n","/**\r\n * Utility pure sulle diagnostiche **ricevute** dal backend: raggruppamento per nodo, gravita'\r\n * peggiore, e la distinzione fra \"da correggere\" e \"non ancora compilabile\".\r\n *\r\n * Qui non nasce nessuna diagnostica e non si giudica nessuna definizione: si legge cio' che il\r\n * backend ha detto e si decide **come renderlo**. La sola informazione interpretata e' il\r\n * `DiagnosticPath`, che il contratto pubblica in **due forme** (con nome del nodo dalla\r\n * validazione, con solo indice dalla deserializzazione): chi legge deve accettarle entrambe,\r\n * altrimenti le diagnostiche del caricamento di un file non sono navigabili.\r\n */\r\n\r\nimport {\r\n Diagnostic,\r\n DiagnosticPathSegment,\r\n DiagnosticSeverity,\r\n DPE_NODE_NOT_COMPILABLE,\r\n} from '../model/dpe-diagnostics.model';\r\nimport { DpeDefinition } from '../model/dpe-definition.model';\r\nimport { dpeAllNodes } from './dpe-graph';\r\nimport { dpeIsNodeCollectionName } from './dpe-node-collection-guard';\r\n\r\n/**\r\n * Il nodo a cui una diagnostica si riferisce, se ricavabile.\r\n *\r\n * Tre strade, nell'ordine: il nome nel primo segmento del percorso; l'argomento canonico\r\n * `node`; l'indice del primo segmento risolto nella collezione corrispondente della\r\n * definizione — che e' il caso della deserializzazione, dove i nomi non sono ancora noti.\r\n * Senza la terza, un `filters[0].sourceName` resterebbe una riga non cliccabile.\r\n *\r\n * La prima e la terza strada valgono solo se la radice del percorso e' una collezione di nodi\r\n * (`contracts.md` §1, normativo; questione aperta 39).\r\n */\r\nexport function dpeDiagnosticNodeName(\r\n diagnostic: Diagnostic,\r\n definition?: DpeDefinition,\r\n): string | undefined {\r\n const first = diagnostic.path?.segments?.[0];\r\n /*\r\n * Il backend emette primi segmenti con un nome che non sono nodi: `parameters/<nome>` dalla\r\n * validazione, `restrictions/<writeback>` e `artifactPlan/<writeback>` dalle utility. Senza il\r\n * controllo il nome del parametro tornava come nodo — riga cliccabile verso un nodo che non\r\n * esiste, e diagnostica sparita da quelle della definizione. `artifactPlan` portava al\r\n * writeback solo per coincidenza di nome, contro l'intento dell'emettitore: smette apposta.\r\n * Il confronto e' esatto con i nomi del modello, senza varianti, perche' il contratto impone\r\n * alle collezioni di nodi la sola grafia del modello in ogni forma del percorso: se un livello\r\n * ne scrivesse un'altra, la navigazione di quel tipo di nodo si spegnerebbe in silenzio, e il\r\n * rimedio e' nel backend, non in un elenco di grafie qui.\r\n */\r\n if (first?.name && dpeIsNodeCollectionName(first.collection)) {\r\n return first.name;\r\n }\r\n const fromArguments = diagnostic.arguments?.['node'];\r\n if (fromArguments) {\r\n return fromArguments;\r\n }\r\n if (!definition || first?.index === undefined) {\r\n return undefined;\r\n }\r\n /*\r\n * Il segmento arriva dal backend e puo' nominare un array della definizione che non e' una\r\n * collezione di nodi: `parameters[i]` ha voci con un `name` e passava `Array.isArray`, quindi\r\n * il nome di un parametro tornava come nodo — riga cliccabile verso un nodo inesistente, e\r\n * diagnostica sparita da quelle della definizione. Il controllo sostituisce `Array.isArray`,\r\n * che proteggeva solo dalle chiavi non array (`name`, `dialect`).\r\n */\r\n if (!dpeIsNodeCollectionName(first.collection)) {\r\n return undefined;\r\n }\r\n return definition[first.collection]?.[first.index]?.name;\r\n}\r\n\r\n/** Il campo in causa, per evidenziarlo nell'ispettore. */\r\nexport function dpeDiagnosticField(diagnostic: Diagnostic): string | undefined {\r\n return diagnostic.path?.field;\r\n}\r\n\r\n/**\r\n * La diagnostica riguarda **quella riga** di una collezione dentro il nodo, e quel campo.\r\n *\r\n * Serve dove un nodo ha piu' righe con lo stesso nome di campo — il caso reale sono le colonne\r\n * calcolate di una trasformazione, che hanno tutte un `expression`. Filtrare per solo nome di\r\n * campo, come fa `issuesOn`, mostrerebbe l'errore della prima formula sotto la terza: il rilievo\r\n * arriva accanto a un testo che non lo ha prodotto, ed e' peggio di non mostrarlo.\r\n *\r\n * Il confronto e' sul segmento del percorso che nomina la collezione, ovunque stia: `contracts.md`\r\n * §1 pubblica il percorso in **due** forme (col nome del nodo dalla validazione, col solo indice\r\n * dalla deserializzazione), e in entrambe il segmento della riga porta l'indice.\r\n */\r\nexport function dpeDiagnosticTargetsItem(\r\n diagnostic: Diagnostic,\r\n collection: string,\r\n index: number,\r\n field: string,\r\n): boolean {\r\n if (dpeDiagnosticField(diagnostic) !== field) {\r\n return false;\r\n }\r\n return (diagnostic.path?.segments ?? []).some(\r\n (segment) => segment.collection === collection && segment.index === index,\r\n );\r\n}\r\n\r\n/**\r\n * `true` per l'unico codice che significa \"hai fatto bene, siamo noi\" (**I10**, ADR 0005):\r\n * `Forecast`, `CustomNode`, costrutto non supportato dal dialetto scelto. Non va mescolato\r\n * con i `DPE_MODEL_*`, che l'utente deve correggere.\r\n */\r\nexport function dpeIsNotCompilable(diagnostic: Diagnostic): boolean {\r\n return diagnostic.code === DPE_NODE_NOT_COMPILABLE;\r\n}\r\n\r\n/**\r\n * `true` quando la diagnostica riguarda le **capacita' del dialetto** e non la definizione in\r\n * se': l'interfaccia deve dire «questo dialetto non lo supporta», perche' lo stesso documento\r\n * su un altro dialetto sarebbe compilabile.\r\n */\r\nexport function dpeIsDialectDiagnostic(diagnostic: Diagnostic): boolean {\r\n return diagnostic.code.startsWith('DPE_DIALECT_');\r\n}\r\n\r\nexport interface DpeNodeDiagnostics {\r\n /** Diagnostiche ancorate al nodo, nell'ordine ricevuto (**I6**: l'elenco e' esaustivo). */\r\n diagnostics: Diagnostic[];\r\n /** La gravita' peggiore fra quelle presenti. */\r\n severity?: DiagnosticSeverity;\r\n /**\r\n * Il nodo e' **valido ma non compilabile**: stato distinto da \"non valido\", e l'unico per\r\n * cui la UI non deve chiedere all'utente di correggere niente.\r\n */\r\n isNotCompilable: boolean;\r\n}\r\n\r\nconst SEVERITY_RANK: Readonly<Record<DiagnosticSeverity, number>> = {\r\n Info: 1,\r\n Warning: 2,\r\n Error: 3,\r\n};\r\n\r\nexport function dpeWorstSeverity(diagnostics: readonly Diagnostic[]): DiagnosticSeverity | undefined {\r\n let worst: DiagnosticSeverity | undefined;\r\n for (const diagnostic of diagnostics) {\r\n if (!worst || SEVERITY_RANK[diagnostic.severity] > SEVERITY_RANK[worst]) {\r\n worst = diagnostic.severity;\r\n }\r\n }\r\n return worst;\r\n}\r\n\r\n/** Raggruppa per nodo. Le diagnostiche non ancorabili a un nodo non si perdono: vedi sotto. */\r\nexport function dpeGroupDiagnosticsByNode(\r\n diagnostics: readonly Diagnostic[],\r\n definition?: DpeDefinition,\r\n): Map<string, DpeNodeDiagnostics> {\r\n const grouped = new Map<string, DpeNodeDiagnostics>();\r\n for (const diagnostic of diagnostics) {\r\n const nodeName = dpeDiagnosticNodeName(diagnostic, definition);\r\n if (nodeName === undefined) {\r\n continue;\r\n }\r\n const entry = grouped.get(nodeName) ?? { diagnostics: [], isNotCompilable: false };\r\n entry.diagnostics.push(diagnostic);\r\n entry.isNotCompilable = entry.isNotCompilable || dpeIsNotCompilable(diagnostic);\r\n entry.severity = dpeWorstSeverity(entry.diagnostics);\r\n grouped.set(nodeName, entry);\r\n }\r\n return grouped;\r\n}\r\n\r\n/**\r\n * Le diagnostiche che **non** appartengono a nessun nodo (radice, parametri, sequenza dei\r\n * writeback, grafo intero). Esistono come funzione a se' perche' il pannello deve mostrarle:\r\n * un `DPE_GRAPH_NO_WRITEBACK` che scompare perche' non ha un nodo a cui attaccarsi sarebbe la\r\n * diagnostica piu' importante di tutte a non arrivare all'utente.\r\n */\r\nexport function dpeDefinitionLevelDiagnostics(\r\n diagnostics: readonly Diagnostic[],\r\n definition?: DpeDefinition,\r\n): Diagnostic[] {\r\n return diagnostics.filter(\r\n (diagnostic) => dpeDiagnosticNodeName(diagnostic, definition) === undefined,\r\n );\r\n}\r\n\r\n/**\r\n * Le parole con cui si legge il primo segmento quando la sua radice non e' una collezione di nodi.\r\n * Le tre radici sono quelle che `contracts.md` §1 dichiara non-nodo; per `restrictions` e\r\n * `artifactPlan` il nome e' **del writeback**, non della cosa, e la preposizione lo dice — per\r\n * questo la preposizione sta solo davanti al nome, e non davanti all'indice.\r\n *\r\n * E' un elenco scritto qui, ma non e' l'opzione C della questione 39 rientrata dalla finestra:\r\n * governa solo il testo, non la navigazione, e una radice che non vi compare non sparisce — si\r\n * legge grezza, come i segmenti successivi. Il difetto che l'elenco puo' avere e' una parola\r\n * italiana mancante, non un elemento nascosto o un nodo inventato.\r\n *\r\n * `Map` e non oggetto letterale: la chiave arriva dal backend, e su un oggetto una radice che si\r\n * chiamasse `constructor` o `toString` troverebbe una funzione del prototipo.\r\n */\r\nconst NON_NODE_ROOT_WORDS: ReadonlyMap<string, { readonly noun: string; readonly preposition?: string }> =\r\n new Map([\r\n ['parameters', { noun: 'parametro' }],\r\n ['restrictions', { noun: 'restrizione', preposition: 'su' }],\r\n ['artifactPlan', { noun: 'piano dell’artefatto', preposition: 'di' }],\r\n ]);\r\n\r\nfunction dpeSegmentLabel(segment: DiagnosticPathSegment): string {\r\n const index = segment.index === undefined ? '' : `[${segment.index}]`;\r\n return `${segment.collection ?? ''}${segment.name ? `/${segment.name}` : index}`;\r\n}\r\n\r\n/**\r\n * La radice non-nodo descritta: col nome se c'e', altrimenti con l'indice se c'e', altrimenti con\r\n * la sola parola. Nessuna delle tre strade restituisce vuoto per una radice conosciuta, ed e' il\r\n * punto: il nome vuoto e l'indice senza nome sono proprio le forme che si scartavano.\r\n */\r\nfunction dpeNonNodeRootLabel(segment: DiagnosticPathSegment): string {\r\n const words = NON_NODE_ROOT_WORDS.get(segment.collection ?? '');\r\n if (words === undefined) {\r\n return dpeSegmentLabel(segment);\r\n }\r\n if (segment.name) {\r\n return [words.noun, words.preposition, segment.name].filter(Boolean).join(' ');\r\n }\r\n return segment.index === undefined ? words.noun : `${words.noun} [${segment.index}]`;\r\n}\r\n\r\n/** Testo leggibile del percorso, per la riga del pannello. */\r\nexport function dpeDiagnosticLocationLabel(\r\n diagnostic: Diagnostic,\r\n definition?: DpeDefinition,\r\n): string {\r\n const node = dpeDiagnosticNodeName(diagnostic, definition);\r\n const [first, ...rest] = diagnostic.path?.segments ?? [];\r\n /*\r\n * Il primo segmento si scartava sempre, perche' si presumeva che fosse il nodo e il nodo e' gia'\r\n * in testa. Da quando `parameters`, `restrictions` e `artifactPlan` non indicano un nodo,\r\n * scartarlo cancellava **quale** parametro o writeback: il messaggio di\r\n * `DPE_MODEL_CONDITIONAL_REQUIRED` non lo nomina, e due parametri senza `valueType` davano due\r\n * righe identiche, `definizione › valueType`. Lo stesso controllo di `dpeDiagnosticNodeName`\r\n * decide qui, cosi' il primo segmento e' o il nodo in testa o la radice descritta, mai tutt'e due\r\n * e mai nessuno.\r\n *\r\n * Il controllo e' sulla sola radice, non sulla presenza del nome: la deserializzazione non\r\n * conosce i nomi (`parameters[0].valueType`), la validazione non ne ha uno per un parametro che\r\n * non lo porta, e `ArtifactPlanValidator` nomina il piano con `\"\"` quando manca proprio il\r\n * `writebackName` di cui si lamenta — tre forme in cui scartare il segmento attribuiva il difetto\r\n * alla definizione. La riga resta non navigabile: lo decide `dpeDiagnosticNodeName`, non questa\r\n * etichetta.\r\n */\r\n const root = first && !dpeIsNodeCollectionName(first.collection) ? dpeNonNodeRootLabel(first) : '';\r\n const head = root.length === 0 ? [node ?? 'definizione'] : [node, root];\r\n const tail = rest.map(dpeSegmentLabel).filter((part) => part.length > 0);\r\n const field = diagnostic.path?.field;\r\n return [...head, ...tail, field].filter(Boolean).join(' › ');\r\n}\r\n\r\n/** I nomi dei nodi presenti nella definizione: serve a chi naviga dal pannello al canvas. */\r\nexport function dpeKnownNodeNames(definition: DpeDefinition): Set<string> {\r\n return new Set(dpeAllNodes(definition).map((located) => located.node.name));\r\n}\r\n","/**\r\n * Le coordinate di un nodo sul canvas — ADR 0013.\r\n *\r\n * Vivono in `extensionData` sotto la chiave namespaced `dpe:layout`, forma `{x, y}`. Il\r\n * backend le conserva senza interpretarle (**I7**), la validazione non le guarda, il\r\n * compilatore non le vede: l'unico livello che gli da' significato e' l'editor.\r\n *\r\n * Due regole dell'ADR sono implementate qui, e sono le sole che contano:\r\n *\r\n * - le coordinate sono **opzionali**: una definizione scritta a mano non le ha e resta\r\n * valida; in loro assenza si esegue dagre;\r\n * - un `dpe:layout` **malformato o parziale si ignora**. Un documento arrivato da un'altra\r\n * versione non deve poter rompere il canvas, e \"meta' coordinata\" non e' una posizione:\r\n * accettarla metterebbe il nodo a `x` giusta e `y` zero, cioe' in un posto che nessuno ha\r\n * scelto.\r\n */\r\n\r\nimport { DpeAnyNode, DpeExtensionData } from '../model/dpe-definition.model';\r\n\r\n/** La chiave e' namespaced perche' `extensionData` e' anche dove atterrano i campi futuri. */\r\nexport const DPE_LAYOUT_KEY = 'dpe:layout';\r\n\r\nexport interface DpeNodePosition {\r\n x: number;\r\n y: number;\r\n}\r\n\r\n/** Legge la posizione, oppure `undefined` se assente o non utilizzabile. */\r\nexport function dpeReadNodePosition(node: DpeAnyNode): DpeNodePosition | undefined {\r\n const raw = node.extensionData?.[DPE_LAYOUT_KEY];\r\n if (raw === null || typeof raw !== 'object') {\r\n return undefined;\r\n }\r\n const candidate = raw as { x?: unknown; y?: unknown };\r\n // `Number.isFinite` e non `typeof === 'number'`: `NaN` e `Infinity` sono numeri per\r\n // TypeScript e coordinate impossibili per il canvas, dove finirebbero in una trasform CSS\r\n // invalida — il nodo semplicemente non si vede, senza nessun errore.\r\n if (!Number.isFinite(candidate.x) || !Number.isFinite(candidate.y)) {\r\n return undefined;\r\n }\r\n return { x: candidate.x as number, y: candidate.y as number };\r\n}\r\n\r\n/** Scrive la posizione producendo un nuovo nodo: il documento resta immutabile. */\r\nexport function dpeWriteNodePosition<TNode extends DpeAnyNode>(\r\n node: TNode,\r\n position: DpeNodePosition,\r\n): TNode {\r\n const extensionData: DpeExtensionData = {\r\n ...(node.extensionData ?? {}),\r\n [DPE_LAYOUT_KEY]: { x: Math.round(position.x), y: Math.round(position.y) },\r\n };\r\n return { ...node, extensionData };\r\n}\r\n\r\n/** Vero quando **tutti** i nodi hanno una posizione: se no, il canvas rifa' il layout. */\r\nexport function dpeHasCompleteLayout(nodes: readonly DpeAnyNode[]): boolean {\r\n return nodes.length > 0 && nodes.every((node) => dpeReadNodePosition(node) !== undefined);\r\n}\r\n","/**\r\n * Catalogo delle categorie di nodo: etichetta italiana, collezione JSON di appartenenza,\r\n * numero di sorgenti ammesse.\r\n *\r\n * **Descrittivo, non normativo.** Serve alla palette e alle etichette dell'interfaccia. Non\r\n * e' validazione: che un `AppendNode` con una sola sorgente sia sbagliato lo dice il backend,\r\n * non questo file, e i due non devono mai diventare due fonti di verita' in disaccordo. I\r\n * conteggi qui sotto servono a decidere **quante porte disegnare**, che e' una domanda di\r\n * rendering.\r\n */\r\n\r\nimport { DPE_NODE_COLLECTIONS, DpeNodeCollectionName } from '../model/dpe-definition.model';\r\n\r\nexport interface DpeNodeCategory {\r\n /** Collezione JSON della radice: e' anche la chiave con cui si legge il documento. */\r\n collection: DpeNodeCollectionName;\r\n /** Etichetta breve, per la palette e per l'intestazione della card sul canvas. */\r\n label: string;\r\n /** Una riga di spiegazione, per il tooltip della palette. */\r\n description: string;\r\n /** Numero minimo di sorgenti che il nodo dichiara (0 per i punti d'ingresso). */\r\n minSources: number;\r\n /** Numero massimo, `undefined` = illimitato (`Append`, `CustomNode`). */\r\n maxSources?: number;\r\n /** Vero per i punti d'ingresso del grafo. */\r\n isSource: boolean;\r\n /** Vero per i sink: producono l'artefatto, non uno schema. */\r\n isSink: boolean;\r\n /**\r\n * Vero dove il backend rifiuta la compilazione pur accettando il modello (**I10**): la UI\r\n * deve poter avvisare **prima** che l'utente costruisca la pipeline intorno a un nodo che\r\n * non arrivera' all'SQL. Non e' una regola di dominio riprodotta: e' l'informazione che\r\n * `docs/domain-model.md` dichiara esplicitamente per queste due categorie, e la\r\n * diagnostica autorevole resta `DPE_NODE_NOT_COMPILABLE` del backend.\r\n */\r\n isNotCompilableInV1: boolean;\r\n}\r\n\r\n/**\r\n * Cio' che il catalogo scrive di una categoria: tutto tranne la collezione, che e' la **chiave**\r\n * della voce. Tenerla anche dentro la voce sarebbe una seconda copia dello stesso fatto, e la\r\n * seconda copia si scosta (ADR 0018): qui non si puo' nemmeno scrivere, perche' il tipo non la\r\n * ammette e il controllo delle proprieta' in eccesso la rifiuta.\r\n */\r\ntype DpeNodeCategoryFacts = Omit<DpeNodeCategory, 'collection'>;\r\n\r\n/**\r\n * Il catalogo, **una voce per collezione**. E' un `Record` sull'unione e non un elenco di voci\r\n * sciolte: il compilatore pretende una voce per **ogni** membro di `DpeNodeCollectionName`, e una\r\n * collezione aggiunta al modello senza voce qui ferma la build («Property '...' is missing»).\r\n * Con l'elenco di prima la stessa omissione compilava e finiva nel ripiego di `dpeNodeCategory`:\r\n * un pozzo dimenticato vi diventa un nodo di mezzo, con il conteggio colonne sulla card e\r\n * «Nessuna colonna in uscita.» nella dialog — due rese sbagliate che non dicono niente.\r\n *\r\n * L'ordine delle chiavi qui non conta e non va letto: l'ordine e' quello di `DPE_NODE_COLLECTIONS`.\r\n */\r\nconst CATEGORY_FACTS: { readonly [K in DpeNodeCollectionName]: DpeNodeCategoryFacts } = {\r\n dataSources: {\r\n label: 'Sorgente',\r\n description: 'Punto d’ingresso: tabella del provider di metadati, CSV o dataset esterno.',\r\n minSources: 0,\r\n maxSources: 0,\r\n isSource: true,\r\n isSink: false,\r\n isNotCompilableInV1: false,\r\n },\r\n filters: {\r\n label: 'Filtro',\r\n description: 'Seleziona le righe con criteri combinati, oppure con un filtro dinamico da parametro.',\r\n minSources: 1,\r\n maxSources: 1,\r\n isSource: false,\r\n isSink: false,\r\n isNotCompilableInV1: false,\r\n },\r\n joins: {\r\n label: 'Join',\r\n description: 'Unisce due sorgenti su una o piu’ chiavi; lo schema di uscita e’ esplicito.',\r\n minSources: 2,\r\n maxSources: 2,\r\n isSource: false,\r\n isSink: false,\r\n isNotCompilableInV1: false,\r\n },\r\n aggregates: {\r\n label: 'Aggregazione',\r\n description: 'Raggruppa e calcola funzioni di aggregazione; senza raggruppamento produce una riga.',\r\n minSources: 1,\r\n maxSources: 1,\r\n isSource: false,\r\n isSink: false,\r\n isNotCompilableInV1: false,\r\n },\r\n transforms: {\r\n label: 'Trasformazione',\r\n description: 'Aggiunge colonne calcolate, ne elimina, oppure calcola valori relativi a una partizione.',\r\n minSources: 1,\r\n maxSources: 1,\r\n isSource: false,\r\n isSink: false,\r\n isNotCompilableInV1: false,\r\n },\r\n appends: {\r\n label: 'Unione',\r\n description: 'Impila N sorgenti; con schemi disgiunti le colonne mancanti diventano nulle.',\r\n minSources: 2,\r\n isSource: false,\r\n isSink: false,\r\n isNotCompilableInV1: false,\r\n },\r\n hierarchyPaths: {\r\n label: 'Percorso gerarchico',\r\n description: 'Risale una gerarchia padre/figlio e produce la colonna di percorso; richiede CTE ricorsive.',\r\n minSources: 1,\r\n maxSources: 1,\r\n isSource: false,\r\n isSink: false,\r\n isNotCompilableInV1: false,\r\n },\r\n forecasts: {\r\n label: 'Previsione',\r\n description: 'Modellata per intero ma non traducibile in SQL nella versione 1.',\r\n minSources: 1,\r\n maxSources: 1,\r\n isSource: false,\r\n isSink: false,\r\n isNotCompilableInV1: true,\r\n },\r\n customNodes: {\r\n label: 'Nodo personalizzato',\r\n description: 'Estensione esterna con schema di uscita dichiarato; non traducibile in SQL nella versione 1.',\r\n minSources: 1,\r\n isSource: false,\r\n isSink: false,\r\n isNotCompilableInV1: true,\r\n },\r\n writebacks: {\r\n label: 'Scrittura',\r\n description: 'Sink: produce l’istruzione SQL per l’host, oppure un file JSON o CSV.',\r\n minSources: 1,\r\n maxSources: 1,\r\n isSource: false,\r\n isSink: true,\r\n isNotCompilableInV1: false,\r\n },\r\n atomicWritebacks: {\r\n label: 'Gruppo atomico',\r\n description: 'Dichiara che alcune scritture vanno applicate dall’host nella stessa transazione.',\r\n minSources: 0,\r\n isSource: false,\r\n isSink: true,\r\n isNotCompilableInV1: false,\r\n },\r\n};\r\n\r\n/**\r\n * Le categorie nell'ordine di `DPE_NODE_COLLECTIONS`, che e' l'ordine della palette. **Derivate,\r\n * non riscritte**: l'ordine vive in un posto solo — il modello, dove un controllo di tipo lo tiene\r\n * totale e senza ripetizioni — e la collezione di ogni categoria e' la stessa variabile con cui se\r\n * ne legge la voce, quindi chiave e campo non hanno modo di divergere.\r\n */\r\nexport const DPE_NODE_CATEGORIES: readonly DpeNodeCategory[] = DPE_NODE_COLLECTIONS.map(\r\n (collection) => ({ collection, ...CATEGORY_FACTS[collection] }),\r\n);\r\n\r\n/** Le stesse istanze dell'elenco: la palette e le card leggono lo stesso oggetto. */\r\nconst BY_COLLECTION = new Map<DpeNodeCollectionName, DpeNodeCategory>(\r\n DPE_NODE_CATEGORIES.map((category) => [category.collection, category]),\r\n);\r\n\r\nexport function dpeNodeCategory(collection: DpeNodeCollectionName): DpeNodeCategory {\r\n const category = BY_COLLECTION.get(collection);\r\n if (!category) {\r\n /*\r\n * Nessun membro di `DpeNodeCollectionName` arriva qui: `CATEGORY_FACTS` e' un `Record`\r\n * sull'unione e `DPE_NODE_COLLECTIONS` e' controllato totale, quindi una voce che manca e' un\r\n * errore di build, non un caso di esecuzione. Ci arriverebbe soltanto un valore che il tipo\r\n * dichiara collezione e che non lo e', e i confini che convertono un dato esterno — il `fData`\r\n * di un rilascio sul canvas, il segmento di percorso di una diagnostica — ora lo controllano\r\n * prima (`dpe-node-collection-guard.ts`). Quindi arrivarci e' un errore di programmazione, e\r\n * si lancia nominando il valore.\r\n *\r\n * Prima qui c'era un ripiego neutro, giustificato dal confine non controllato: rendeva un\r\n * pozzo come nodo di mezzo, in silenzio. Tolto il confine scoperto, si toglie la tolleranza che\r\n * lo assorbiva.\r\n */\r\n throw new Error(`dpeNodeCategory: «${String(collection)}» non e’ una collezione di nodi.`);\r\n }\r\n return category;\r\n}\r\n\r\n/** Nomi dei dialetti per il selettore. La libreria conosce **solo** il nome (ADR 0009). */\r\nexport const DPE_DIALECT_LABELS: Readonly<Record<string, string>> = {\r\n Sqlite: 'SQLite',\r\n SqlServer: 'SQL Server',\r\n PostgreSql: 'PostgreSQL',\r\n Oracle: 'Oracle',\r\n};\r\n","/**\r\n * Creazione di un nodo nuovo: nome candidato e valori iniziali per categoria.\r\n *\r\n * Non e' validazione e non e' dominio. Serve a una cosa sola: un nodo appena rilasciato sul\r\n * canvas deve essere **rappresentabile** e distinguibile, cioe' avere un nome e un'etichetta.\r\n * Tutto il resto lo compila l'utente nell'ispettore, e cosa sia obbligatorio lo dice il\r\n * backend.\r\n *\r\n * Il nome candidato e' unico rispetto ai nomi gia' presenti con confronto **esatto**\r\n * (`contracts.md` §2: unicita' verificata `OrdinalIgnoreCase`, risoluzione `Ordinal`). Qui il\r\n * confronto e' insensibile al caso di proposito: proporre `Filtro2` accanto a un `filtro2`\r\n * esistente creerebbe un `DPE_MODEL_NAME_DUPLICATE` a nome dell'editor, non dell'utente.\r\n */\r\n\r\nimport {\r\n DpeAnyNode,\r\n DpeDefinition,\r\n DpeNodeCollectionMap,\r\n DpeNodeCollectionName,\r\n} from '../model/dpe-definition.model';\r\nimport { dpeAllNodes } from './dpe-graph';\r\nimport { dpeNodeCategory } from './dpe-node-catalog';\r\n\r\n/** Radice del nome proposto per categoria: identificatori, quindi senza spazi ne' accenti. */\r\nconst NAME_STEM: Readonly<Record<DpeNodeCollectionName, string>> = {\r\n dataSources: 'Sorgente',\r\n filters: 'Filtro',\r\n joins: 'Join',\r\n aggregates: 'Aggregazione',\r\n transforms: 'Trasformazione',\r\n appends: 'Unione',\r\n hierarchyPaths: 'Gerarchia',\r\n forecasts: 'Previsione',\r\n customNodes: 'NodoCustom',\r\n writebacks: 'Scrittura',\r\n atomicWritebacks: 'GruppoAtomico',\r\n};\r\n\r\n/** Il primo nome libero della forma `<radice><n>`. */\r\nexport function dpeProposeNodeName(\r\n definition: DpeDefinition,\r\n collection: DpeNodeCollectionName,\r\n): string {\r\n const used = new Set(dpeAllNodes(definition).map((located) => located.node.name.toLowerCase()));\r\n const stem = NAME_STEM[collection];\r\n let index = 1;\r\n while (used.has(`${stem}${index}`.toLowerCase())) {\r\n index += 1;\r\n }\r\n return `${stem}${index}`;\r\n}\r\n\r\n/**\r\n * Il nodo iniziale di una categoria. I campi obbligatori senza un valore sensato restano\r\n * **assenti**, non riempiti con un segnaposto: un `tableName` finto passerebbe la forma e\r\n * fallirebbe sul provider di metadati con un errore che l'utente non ha causato.\r\n */\r\nexport function dpeCreateNode<K extends DpeNodeCollectionName>(\r\n definition: DpeDefinition,\r\n collection: K,\r\n): DpeNodeCollectionMap[K] {\r\n const name = dpeProposeNodeName(definition, collection);\r\n const label = `${dpeNodeCategory(collection).label} ${name.replace(/^\\D+/, '')}`;\r\n const base = { name, label };\r\n\r\n switch (collection) {\r\n case 'dataSources':\r\n return { ...base, kind: 'Table', fields: [] } as unknown as DpeNodeCollectionMap[K];\r\n case 'joins':\r\n return { ...base, kind: 'Inner', keys: [], fields: [] } as unknown as DpeNodeCollectionMap[K];\r\n case 'aggregates':\r\n return { ...base, fields: [] } as unknown as DpeNodeCollectionMap[K];\r\n case 'transforms':\r\n return { ...base, kind: 'Expression', expressionFields: [] } as unknown as DpeNodeCollectionMap[K];\r\n case 'appends':\r\n // Due posizioni vuote: un'unione nasce con due ingressi da collegare, ed e' anche cio'\r\n // che fa comparire due porte sul canvas invece di nessuna.\r\n return { ...base, sources: ['', ''] } as unknown as DpeNodeCollectionMap[K];\r\n case 'customNodes':\r\n return { ...base, sources: [''], extensionName: '', outputFields: [] } as unknown as DpeNodeCollectionMap[K];\r\n case 'forecasts':\r\n return { ...base, aggregationFields: [] } as unknown as DpeNodeCollectionMap[K];\r\n case 'writebacks':\r\n return {\r\n ...base,\r\n target: 'Database',\r\n operation: 'Insert',\r\n sequence: dpeProposeWritebackSequence(definition),\r\n fields: [],\r\n } as unknown as DpeNodeCollectionMap[K];\r\n case 'atomicWritebacks':\r\n return {\r\n ...base,\r\n sequence: dpeProposeWritebackSequence(definition),\r\n relationships: [],\r\n } as unknown as DpeNodeCollectionMap[K];\r\n default:\r\n return base as unknown as DpeNodeCollectionMap[K];\r\n }\r\n}\r\n\r\n/**\r\n * La sequenza dei sink e' univoca fra writeback e gruppi atomici: si propone la prima libera.\r\n * L'unicita' la verifica il backend (`DPE_WRITEBACK_SEQUENCE_DUPLICATE`) — questo e' solo un\r\n * valore iniziale che non collide, non un controllo.\r\n */\r\nexport function dpeProposeWritebackSequence(definition: DpeDefinition): number {\r\n const used = new Set<number>();\r\n for (const node of [...(definition.writebacks ?? []), ...(definition.atomicWritebacks ?? [])]) {\r\n used.add(node.sequence);\r\n }\r\n let sequence = 1;\r\n while (used.has(sequence)) {\r\n sequence += 1;\r\n }\r\n return sequence;\r\n}\r\n\r\n/** Etichetta di sintesi mostrata sulla card del nodo, sotto il nome. */\r\nexport function dpeNodeSubtitle(collection: DpeNodeCollectionName, node: DpeAnyNode): string | null {\r\n const record = node as unknown as Record<string, unknown>;\r\n switch (collection) {\r\n case 'dataSources':\r\n return (record['tableName'] as string) || (record['externalDatasetName'] as string) || null;\r\n case 'joins':\r\n return (record['kind'] as string) || null;\r\n case 'transforms':\r\n return (record['kind'] as string) || null;\r\n case 'aggregates': {\r\n const fields = (record['fields'] as unknown[] | undefined)?.length ?? 0;\r\n return fields ? `${fields} ${fields === 1 ? 'funzione' : 'funzioni'}` : null;\r\n }\r\n case 'filters':\r\n return record['isDynamic'] === true ? 'dinamico' : (record['condition'] as string) || null;\r\n case 'writebacks': {\r\n const target = record['target'] as string | undefined;\r\n const table = record['tableName'] as string | undefined;\r\n return [target, table].filter(Boolean).join(' · ') || null;\r\n }\r\n case 'atomicWritebacks': {\r\n const relationships = (record['relationships'] as unknown[] | undefined)?.length ?? 0;\r\n return relationships\r\n ? `${relationships} ${relationships === 1 ? 'relazione' : 'relazioni'}`\r\n : null;\r\n }\r\n default:\r\n return null;\r\n }\r\n}\r\n","/**\r\n * Lo stato dell'editor: la definizione in memoria e' **la fonte di verita'**.\r\n *\r\n * `@Injectable()` **senza `providedIn`**: e' un provider del componente radice dell'editor,\r\n * non della root. Due editor sulla stessa pagina devono poter editare due definizioni\r\n * indipendenti, e con un servizio in root ne condividerebbero una — compresi undo/redo e\r\n * selezione.\r\n *\r\n * Stato **immutabile e unidirezionale**: ogni mutazione produce un nuovo valore della\r\n * definizione, nessuno muta l'oggetto in luogo. Da li' viene gratis l'undo/redo (istantanee\r\n * JSON), che non e' una funzionalita' aggiunta dopo ma una conseguenza.\r\n *\r\n * Cosa **non** fa questo store, e non e' una dimenticanza: non valida, non calcola schemi,\r\n * non risolve riferimenti, non sa cosa un dialetto supporti. Diagnostiche, schemi, ordine di\r\n * esecuzione e sequenza dei writeback **arrivano dal backend** e qui si depositano\r\n * (`applyValidateResult`). L'unica logica presente e' strutturale: dove vivono i riferimenti\r\n * e come si riscrivono, che sta in `dpe-graph.ts`.\r\n */\r\n\r\nimport { Injectable, computed, signal } from '@angular/core';\r\n\r\nimport {\r\n DPE_NODE_COLLECTIONS,\r\n DpeAnyNode,\r\n DpeDefinition,\r\n DpeNodeCollectionMap,\r\n DpeNodeCollectionName,\r\n SqlDialectKind,\r\n} from '../model/dpe-definition.model';\r\nimport { Diagnostic } from '../model/dpe-diagnostics.model';\r\nimport {\r\n DpeCompileResult,\r\n DpeNodeSchemaInfo,\r\n DpeValidateResult,\r\n DpeWritebackStep,\r\n} from '../model/dpe-api.model';\r\nimport {\r\n DpeGraph,\r\n dpeAllNodes,\r\n dpeBuildGraph,\r\n dpeCountReferences,\r\n dpeFindNode,\r\n dpeRenameNode,\r\n dpeWriteSourceReference,\r\n} from './dpe-graph';\r\nimport {\r\n DpeNodeDiagnostics,\r\n dpeDefinitionLevelDiagnostics,\r\n dpeGroupDiagnosticsByNode,\r\n} from './dpe-diagnostics.util';\r\nimport { DpeNodePosition, dpeReadNodePosition, dpeWriteNodePosition } from './dpe-node-layout';\r\nimport { dpeCreateNode } from './dpe-node-factory';\r\n\r\n/** Profondita' della cronologia: oltre, le istantanee piu' vecchie si buttano. */\r\nconst HISTORY_LIMIT = 100;\r\n\r\n/**\r\n * Una definizione vuota ma **leggibile**: `formatVersion` e `dialect` non sono opzionali.\r\n *\r\n * **Serve a `createDefinition`, non a montare l'editor.** E' il documento minimo che si manda al\r\n * backend per far nascere una definizione nel catalogo (ADR 0019), e da li' in poi quella che si\r\n * edita e' la definizione **caricata con `load(nome)`**.\r\n *\r\n * Montare l'editor su un documento costruito qui, o su uno costruito a mano senza nome, produce due\r\n * sintomi che sembrano scollegati e sono la stessa cosa: la validazione risponde\r\n * `DPE_MODEL_REQUIRED_MISSING` su `name` e `label` — per il backend una stringa vuota e un campo\r\n * assente sono lo stesso caso — e il salvataggio non ha una chiave per la rotta\r\n * `PUT /definitions/{name}`. L'editor lo dichiara in cima con un banner e disabilita «Salva», ma la\r\n * correzione e' a monte: creare dal catalogo e caricare.\r\n */\r\nexport function dpeEmptyDefinition(\r\n name: string,\r\n label: string,\r\n dialect: SqlDialectKind,\r\n): DpeDefinition {\r\n return { formatVersion: 1, name, label, dialect };\r\n}\r\n\r\n@Injectable()\r\nexport class DpeDocumentStore {\r\n private readonly _definition = signal<DpeDefinition>(dpeEmptyDefinition('', '', 'Sqlite'));\r\n private readonly _validateResult = signal<DpeValidateResult | undefined>(undefined);\r\n private readonly _compileResult = signal<DpeCompileResult | undefined>(undefined);\r\n /**\r\n * I nodi selezionati, in ordine di gesto. E' una **lista** e non un nome singolo perche' il\r\n * canvas sa selezionare piu' nodi (riquadro, `Ctrl+A`) e trascinarli insieme: tenere qui un solo\r\n * nome costringerebbe la libreria a conservare gli altri, cioe' a essere di nuovo autorevole\r\n * sulla selezione.\r\n */\r\n private readonly _selectedNodeNames = signal<readonly string[]>([]);\r\n /**\r\n * La dialog di modifica del nodo e' aperta. **Un booleano**, non un secondo nome di nodo: il\r\n * nodo su cui si sta agendo resta `selectedNodeName()`, e due registrazioni dello stesso fatto\r\n * sono esattamente cio' che questo store dichiara di non voler avere (ci ha gia' rimesso una\r\n * volta con la doppia selezione di @foblex/flow).\r\n *\r\n * Non entra nella cronologia: aprire una finestra non e' una modifica del documento, e un undo\r\n * che aprisse o chiudesse finestre sarebbe un comando che fa due cose.\r\n */\r\n private readonly _isNodeEditorOpen = signal(false);\r\n private readonly _past = signal<readonly string[]>([]);\r\n private readonly _future = signal<readonly string[]>([]);\r\n /**\r\n * Istantanea di riferimento: l'ultimo stato **salvato**. Non e' un contatore di operazioni.\r\n * Contare le mutazioni sembra equivalente e non lo e': dopo un `undo` il contatore sale, cioe'\r\n * dichiara piu' modifiche di prima proprio quando l'utente ne ha disfatta una. Serve a decidere\r\n * se salvare, quindi deve rispondere alla domanda giusta — \"differisce da cio' che e' salvato?\"\r\n * — e tornare a \"no\" quando si annulla fino allo stato di partenza.\r\n */\r\n private readonly _savedSnapshot = signal<string>(\r\n JSON.stringify(dpeEmptyDefinition('', '', 'Sqlite')),\r\n );\r\n /**\r\n * Vero da quando un documento e' stato **caricato**. Distingue il segnaposto del costruttore da\r\n * una definizione vera, e serve a chi non deve lavorare sul segnaposto: la validazione non lo\r\n * manda al backend (`DpeValidationService`), perche' la risposta sarebbero i quattro rilievi di\r\n * una definizione vuota — `name`, `label`, `dataSources` e nessun writeback — che descrivono il\r\n * segnaposto e non il lavoro dell'utente, e che dall'editor nessuno puo' correggere.\r\n */\r\n private readonly _hasDocument = signal(false);\r\n\r\n /** La definizione corrente: unica fonte di verita' per canvas, ispettori e pannelli. */\r\n readonly definition = this._definition.asReadonly();\r\n\r\n /** Vero da quando `load` ha portato un documento: prima c'e' solo il segnaposto. */\r\n readonly hasDocument = this._hasDocument.asReadonly();\r\n\r\n /** Nodi e archi derivati dalla definizione. Ricalcolati solo quando la definizione cambia. */\r\n readonly graph = computed<DpeGraph>(() => dpeBuildGraph(this._definition()));\r\n\r\n readonly selectedNodeNames = this._selectedNodeNames.asReadonly();\r\n\r\n /** Insieme dei selezionati: il canvas lo interroga una volta per nodo a ogni render. */\r\n readonly selectedNodeNameSet = computed<ReadonlySet<string>>(\r\n () => new Set(this._selectedNodeNames()),\r\n );\r\n\r\n /**\r\n * Il nodo che l'ispettore mostra: il **primo** della selezione. Un ispettore per piu' nodi\r\n * insieme mostrerebbe campi che appartengono a nodi diversi, che e' peggio di non mostrarne.\r\n */\r\n readonly selectedNodeName = computed<string | undefined>(() => this._selectedNodeNames().at(0));\r\n\r\n readonly selectedNode = computed<DpeAnyNode | undefined>(() => {\r\n const name = this.selectedNodeName();\r\n return name === undefined ? undefined : dpeFindNode(this._definition(), name)?.node;\r\n });\r\n\r\n /**\r\n * La dialog di modifica del nodo e' aperta. Non e' una seconda identita': il nodo e'\r\n * `selectedNodeName()`.\r\n */\r\n readonly isNodeEditorOpen = this._isNodeEditorOpen.asReadonly();\r\n\r\n // --- cio' che arriva dal backend ------------------------------------------\r\n\r\n readonly validateResult = this._validateResult.asReadonly();\r\n readonly compileResult = this._compileResult.asReadonly();\r\n\r\n readonly diagnostics = computed<readonly Diagnostic[]>(\r\n () => this._validateResult()?.diagnostics ?? [],\r\n );\r\n\r\n readonly schemas = computed<readonly DpeNodeSchemaInfo[]>(\r\n () => this._validateResult()?.schemas ?? [],\r\n );\r\n\r\n readonly executionOrder = computed<readonly string[]>(\r\n () => this._validateResult()?.executionOrder ?? [],\r\n );\r\n\r\n readonly writebackSequence = computed<readonly DpeWritebackStep[]>(\r\n () => this._validateResult()?.writebackSequence ?? [],\r\n );\r\n\r\n /** Schema per nome di nodo: la vista dello schema lo cerca per nodo, non per posizione. */\r\n readonly schemaByNode = computed<ReadonlyMap<string, DpeNodeSchemaInfo>>(() => {\r\n const map = new Map<string, DpeNodeSchemaInfo>();\r\n for (const schema of this.schemas()) {\r\n map.set(schema.nodeName, schema);\r\n }\r\n return map;\r\n });\r\n\r\n /**\r\n * Diagnostiche raggruppate per nodo, piu' quelle che **non** appartengono a nessun nodo\r\n * (radice, parametri, grafo intero): tenerle separate e' l'unico modo perche' un\r\n * `DPE_GRAPH_NO_WRITEBACK` non scompaia solo perche' non ha un nodo a cui attaccarsi.\r\n */\r\n readonly diagnosticsByNode = computed<ReadonlyMap<string, DpeNodeDiagnostics>>(() =>\r\n dpeGroupDiagnosticsByNode(this.diagnostics(), this._definition()),\r\n );\r\n\r\n readonly definitionDiagnostics = computed<readonly Diagnostic[]>(() =>\r\n dpeDefinitionLevelDiagnostics(this.diagnostics(), this._definition()),\r\n );\r\n\r\n /** Posizioni lette da `dpe:layout` (ADR 0013): assenti dove il documento non le porta. */\r\n readonly positions = computed<ReadonlyMap<string, DpeNodePosition>>(() => {\r\n const map = new Map<string, DpeNodePosition>();\r\n for (const located of dpeAllNodes(this._definition())) {\r\n const position = dpeReadNodePosition(located.node);\r\n if (position) {\r\n map.set(located.node.name, position);\r\n }\r\n }\r\n return map;\r\n });\r\n\r\n readonly canUndo = computed(() => this._past().length > 0);\r\n readonly canRedo = computed(() => this._future().length > 0);\r\n\r\n /**\r\n * Vero quando il documento differisce dall'ultimo salvataggio. Il confronto e' fra istantanee\r\n * JSON — la stessa moneta dell'undo/redo — e **non** un conteggio di differenze: un elenco di\r\n * differenze costa quanto un diff strutturale, e nessuno lo legge come numero.\r\n *\r\n * Confronto testuale, quindi sensibile all'ordine delle chiavi. Le mutazioni producono nuovi\r\n * valori per spread, che l'ordine lo conserva, e l'undo riparte dalla stessa serializzazione:\r\n * l'unico esito possibile di una divergenza d'ordine sarebbe un falso \"modificata\", cioe' un\r\n * salvataggio in piu' e mai una perdita.\r\n */\r\n readonly isDirty = computed(() => JSON.stringify(this._definition()) !== this._savedSnapshot());\r\n\r\n // -------------------------------------------------------------------------\r\n // Caricamento e risultati del backend\r\n // -------------------------------------------------------------------------\r\n\r\n /**\r\n * Sostituisce la definizione. Azzera la cronologia: le istantanee della definizione\r\n * precedente non sono passi di quella nuova, e un undo che riportasse a un altro documento\r\n * sarebbe una perdita di dati che l'utente non ha chiesto.\r\n */\r\n load(definition: DpeDefinition): void {\r\n this._definition.set(definition);\r\n this._hasDocument.set(true);\r\n // Cio' che e' appena arrivato **e'** lo stato salvato: aprire un documento non lo sporca.\r\n this._savedSnapshot.set(JSON.stringify(definition));\r\n this._past.set([]);\r\n this._future.set([]);\r\n this._selectedNodeNames.set([]);\r\n // Una dialog rimasta aperta guarderebbe un nodo di **un'altra** definizione: il nome potrebbe\r\n // anche esistere qui, e allora si starebbe modificando un nodo che nessuno ha aperto. E' il\r\n // caso che si dimentica, perche' non fallisce — mostra la cosa sbagliata.\r\n this._isNodeEditorOpen.set(false);\r\n this._validateResult.set(undefined);\r\n this._compileResult.set(undefined);\r\n }\r\n\r\n /** Deposita il risultato della validazione. Nessuna interpretazione: si mostra cio' che c'e'. */\r\n applyValidateResult(result: DpeValidateResult | undefined): void {\r\n this._validateResult.set(result);\r\n }\r\n\r\n applyCompileResult(result: DpeCompileResult | undefined): void {\r\n this._compileResult.set(result);\r\n }\r\n\r\n /**\r\n * L'ospite ha salvato: da qui in poi il riferimento e' il documento **effettivamente\r\n * persistito**, che si passa come argomento.\r\n *\r\n * Il parametro non e' un vezzo: un salvataggio e' asincrono, e nel frattempo l'utente puo'\r\n * avere continuato a scrivere. Fissando il riferimento sullo stato *corrente* si dichiarerebbe\r\n * salvato anche cio' che e' stato scritto dopo l'invio, cioe' esattamente il lavoro che si\r\n * perderebbe. Omesso, vale lo stato corrente: e' il caso di chi salva in modo sincrono.\r\n */\r\n markSaved(saved?: DpeDefinition): void {\r\n this._savedSnapshot.set(JSON.stringify(saved ?? this._definition()));\r\n }\r\n\r\n /**\r\n * **L'unico** punto che decide quale nodo e' selezionato. La selezione ha una sola fonte di\r\n * verita', come la definizione: la classe nel DOM e lo stato di selezione della libreria di\r\n * canvas ne **derivano**, non la stabiliscono. Due scritture indipendenti sullo stesso fatto\r\n * producono due nodi selezionati insieme e un utente che non sa su cosa sta agendo.\r\n */\r\n select(nodeName: string | undefined): void {\r\n this._selectedNodeNames.set(nodeName === undefined ? [] : [nodeName]);\r\n }\r\n\r\n /** Selezione multipla (riquadro, `Ctrl+A`): la lista che arriva dal gesto, senza duplicati. */\r\n selectMany(nodeNames: readonly string[]): void {\r\n this._selectedNodeNames.set(Array.from(new Set(nodeNames)));\r\n }\r\n\r\n /**\r\n * Seleziona **solo** quel nodo e apre la dialog di modifica.\r\n *\r\n * La selezione **collassa a un nodo solo**: il gesto «apri questo» dichiara su chi si sta\r\n * agendo, e la regola gia' in vigore e' che con piu' di un nodo selezionato non si mostrano i\r\n * campi di uno.\r\n *\r\n * Le due scritture sono **esplicite** e non conseguenza di un `effect`: un effect che scrive\r\n * signal renderebbe l'ordine di questi due fatti dipendente dalla schedulazione, cioe' non\r\n * testabile.\r\n */\r\n openNodeEditor(nodeName: string): void {\r\n this.select(nodeName);\r\n this._isNodeEditorOpen.set(true);\r\n }\r\n\r\n closeNodeEditor(): void {\r\n this._isNodeEditorOpen.set(false);\r\n }\r\n\r\n // -------------------------------------------------------------------------\r\n // Mutazioni — sincrone e granulari\r\n // -------------------------------------------------------------------------\r\n\r\n /**\r\n * Proprieta' della radice: etichetta, descrizione, dialetto, parametri.\r\n *\r\n * **`name` e `formatVersion` non sono nell'insieme**, e il tipo lo impedisce invece di dirlo in un\r\n * commento. `name` e' la chiave del catalogo: rinominare e' creare-e-cancellare (ADR 0019), non\r\n * una modifica del documento — e `Partial<DpeDefinition>` permetteva `{ name: undefined }`, che\r\n * lasciava nel documento una chiave senza valore. Da li' il salvataggio partiva verso\r\n * `PUT /definitions/undefined`. `formatVersion` e' del formato, non dell'autore.\r\n */\r\n patchDefinition(patch: Partial<Omit<DpeDefinition, 'name' | 'formatVersion'>>): void {\r\n this.commit({ ...this._definition(), ...patch });\r\n }\r\n\r\n addNode<K extends DpeNodeCollectionName>(collection: K, node: DpeNodeCollectionMap[K]): void {\r\n const definition = this._definition();\r\n const existing = (definition[collection] ?? []) as DpeNodeCollectionMap[K][];\r\n this.commit(this.withCollection(definition, collection, [...existing, node]));\r\n }\r\n\r\n updateNode<K extends DpeNodeCollectionName>(\r\n collection: K,\r\n nodeName: string,\r\n patch: Partial<DpeNodeCollectionMap[K]>,\r\n ): void {\r\n const definition = this._definition();\r\n const existing = (definition[collection] ?? []) as DpeNodeCollectionMap[K][];\r\n let changed = false;\r\n const next = existing.map((node) => {\r\n if (node.name !== nodeName) {\r\n return node;\r\n }\r\n changed = true;\r\n // `name` non si cambia da qui: rinominare e' un refactoring globale e passa da\r\n // `renameNode`. Lasciarlo passare in un patch produrrebbe un documento con tutti i\r\n // riferimenti a valle rotti e nessun segnale.\r\n const { name: _ignored, ...rest } = patch as Partial<DpeNodeCollectionMap[K]> & { name?: string };\r\n void _ignored;\r\n return { ...node, ...rest };\r\n });\r\n if (!changed) {\r\n return;\r\n }\r\n this.commit(this.withCollection(definition, collection, next));\r\n }\r\n\r\n /**\r\n * Rimuove un nodo. I riferimenti a valle **restano** e diventano riferimenti rotti: e' cio'\r\n * che il documento dice davvero, e il backend li segnala (`DPE_REF_UNKNOWN_NODE`). Pulirli\r\n * qui vorrebbe dire decidere al posto dell'utente che quel filtro non serve piu'.\r\n */\r\n removeNode(nodeName: string): void {\r\n const definition = this._definition();\r\n const located = dpeFindNode(definition, nodeName);\r\n if (!located) {\r\n return;\r\n }\r\n // Si legge **prima** della potatura della selezione: dopo, `selectedNodeName()` e' gia' un\r\n // altro nodo (o nessuno) e non si saprebbe piu' se la dialog stava guardando questo.\r\n const wasOpenOnIt = this._isNodeEditorOpen() && this.selectedNodeName() === nodeName;\r\n const existing = (definition[located.collection] ?? []) as DpeAnyNode[];\r\n const next = existing.filter((node) => node.name !== nodeName);\r\n this.commit(this.withCollection(definition, located.collection, next));\r\n // Un nome che non esiste piu' non puo' restare selezionato: sarebbe una selezione che punta\r\n // al vuoto. Si riscrive solo se serve, perche' un nuovo array uguale al precedente farebbe\r\n // ripartire per nulla chi osserva la selezione.\r\n const selected = this._selectedNodeNames();\r\n if (selected.includes(nodeName)) {\r\n this._selectedNodeNames.set(selected.filter((name) => name !== nodeName));\r\n }\r\n // Il bersaglio della dialog e' svanito: dopo l'eliminazione non c'e' piu' niente da\r\n // modificare. La chiusura sta qui e non nel componente perche' «Elimina il nodo» sta nel\r\n // piede della dialog, e un componente che si chiude da se' dopo aver chiesto una mutazione\r\n // sarebbe una seconda regola sullo stesso fatto.\r\n if (wasOpenOnIt) {\r\n this._isNodeEditorOpen.set(false);\r\n }\r\n }\r\n\r\n /**\r\n * L'**unico** punto che rinomina un nodo (**I5**): delega alla funzione pura, che aggiorna\r\n * ogni riferimento a valle passando dall'unico elenco di slot.\r\n */\r\n renameNode(oldName: string, newName: string): void {\r\n if (oldName === newName || newName.length === 0) {\r\n return;\r\n }\r\n this.commit(dpeRenameNode(this._definition(), oldName, newName));\r\n // La selezione segue la rinomina: identifica un nodo per nome, e il nome e' cambiato.\r\n const selected = this._selectedNodeNames();\r\n if (selected.includes(oldName)) {\r\n this._selectedNodeNames.set(selected.map((name) => (name === oldName ? newName : name)));\r\n }\r\n }\r\n\r\n /** Quante occorrenze toccherebbe una rinomina: si dice prima, non dopo. */\r\n countReferences(nodeName: string): number {\r\n return dpeCountReferences(this._definition(), nodeName);\r\n }\r\n\r\n /**\r\n * Crea un nodo della categoria data nella posizione indicata e restituisce il nome\r\n * assegnato, cosi' chi lo ha creato puo' selezionarlo subito.\r\n */\r\n createNodeAt<K extends DpeNodeCollectionName>(collection: K, position: DpeNodePosition): string {\r\n const definition = this._definition();\r\n const node = dpeWriteNodePosition(dpeCreateNode(definition, collection), position);\r\n const existing = (definition[collection] ?? []) as DpeNodeCollectionMap[K][];\r\n this.commit(this.withCollection(definition, collection, [...existing, node]));\r\n return node.name;\r\n }\r\n\r\n /**\r\n * Sposta un nodo scrivendo `dpe:layout`. `silent` non aggiunge un passo di cronologia:\r\n * trascinare cinque nodi insieme e' **un** gesto, e cinque passi di undo per un solo\r\n * trascinamento sono cinque passi che l'utente non ha fatto.\r\n */\r\n moveNode(nodeName: string, position: DpeNodePosition, options?: { silent?: boolean }): void {\r\n const definition = this._definition();\r\n const located = dpeFindNode(definition, nodeName);\r\n if (!located) {\r\n return;\r\n }\r\n const existing = (definition[located.collection] ?? []) as DpeAnyNode[];\r\n const next = existing.map((node) =>\r\n node.name === nodeName ? dpeWriteNodePosition(node, position) : node,\r\n );\r\n const updated = this.withCollection(definition, located.collection, next);\r\n if (options?.silent) {\r\n this._definition.set(updated);\r\n return;\r\n }\r\n this.commit(updated);\r\n }\r\n\r\n /**\r\n * Applica un layout calcolato **su comando dell'utente** («Riordina»): un passo di cronologia\r\n * per l'intero riordino, e il documento risulta modificato, perche' lo e'.\r\n */\r\n applyPositions(positions: ReadonlyMap<string, DpeNodePosition>): void {\r\n this.commit(this.withPositions(this._definition(), positions));\r\n }\r\n\r\n /**\r\n * Layout **iniziale** di un documento che non porta coordinate (ADR 0013: sono opzionali).\r\n *\r\n * Non e' una modifica dell'utente: non entra nella cronologia e **ri-fissa l'istantanea di\r\n * riferimento**, cosi' aprire un documento non produce «modificata». Un indicatore che dice\r\n * «modificata» appena si apre insegna a ignorarlo, e un indicatore ignorato e' peggio di\r\n * nessun indicatore. Il prezzo accettato: chi chiude senza salvare perde le coordinate\r\n * calcolate, che dagre rigenera in modo deterministico.\r\n *\r\n * Il riferimento si sposta **solo** se il documento non era gia' modificato: se l'utente ha\r\n * scritto qualcosa, un layout non puo' dichiarare salvato il suo lavoro.\r\n */\r\n applyInitialPositions(positions: ReadonlyMap<string, DpeNodePosition>): void {\r\n const wasClean = !this.isDirty();\r\n const next = this.withPositions(this._definition(), positions);\r\n this._definition.set(next);\r\n if (wasClean) {\r\n this._savedSnapshot.set(JSON.stringify(next));\r\n }\r\n }\r\n\r\n /**\r\n * Collega una porta di ingresso di `targetNodeName` al nodo `sourceName`. Non verifica che\r\n * il collegamento sia lecito: cicli (**I2**) e riferimenti a valle (**I3**) li rifiuta il\r\n * backend, e l'errore compare subito perche' la validazione parte dopo la mutazione.\r\n */\r\n connect(\r\n targetNodeName: string,\r\n slot: string,\r\n sourceName: string,\r\n index?: number,\r\n ): void {\r\n this.writeReference(targetNodeName, slot, index, sourceName);\r\n }\r\n\r\n /** Scollega una porta: lo slot torna vuoto, non a stringa vuota. */\r\n disconnect(targetNodeName: string, slot: string, index?: number): void {\r\n this.writeReference(targetNodeName, slot, index, undefined);\r\n }\r\n\r\n // -------------------------------------------------------------------------\r\n // Cronologia\r\n // -------------------------------------------------------------------------\r\n\r\n undo(): void {\r\n const past = this._past();\r\n if (past.length === 0) {\r\n return;\r\n }\r\n const snapshot = past[past.length - 1];\r\n this._past.set(past.slice(0, -1));\r\n this._future.set([...this._future(), JSON.stringify(this._definition())]);\r\n this._definition.set(JSON.parse(snapshot) as DpeDefinition);\r\n }\r\n\r\n redo(): void {\r\n const future = this._future();\r\n if (future.length === 0) {\r\n return;\r\n }\r\n const snapshot = future[future.length - 1];\r\n this._future.set(future.slice(0, -1));\r\n this._past.set([...this._past(), JSON.stringify(this._definition())]);\r\n this._definition.set(JSON.parse(snapshot) as DpeDefinition);\r\n }\r\n\r\n // -------------------------------------------------------------------------\r\n // Interno\r\n // -------------------------------------------------------------------------\r\n\r\n private writeReference(\r\n targetNodeName: string,\r\n slot: string,\r\n index: number | undefined,\r\n sourceName: string | undefined,\r\n ): void {\r\n const definition = this._definition();\r\n const located = dpeFindNode(definition, targetNodeName);\r\n if (!located) {\r\n return;\r\n }\r\n const existing = (definition[located.collection] ?? []) as DpeAnyNode[];\r\n const next = existing.map((node) =>\r\n node.name === targetNodeName\r\n ? dpeWriteSourceReference(located.collection, node, slot, index, sourceName)\r\n : node,\r\n );\r\n this.commit(this.withCollection(definition, located.collection, next));\r\n }\r\n\r\n /** Scrive le posizioni su tutte le collezioni producendo una nuova definizione. */\r\n private withPositions(\r\n definition: DpeDefinition,\r\n positions: ReadonlyMap<string, DpeNodePosition>,\r\n ): DpeDefinition {\r\n let next = definition;\r\n for (const collection of DPE_NODE_COLLECTIONS) {\r\n const nodes = next[collection] as DpeAnyNode[] | undefined;\r\n if (!nodes || nodes.length === 0) {\r\n continue;\r\n }\r\n const updated = nodes.map((node) => {\r\n const position = positions.get(node.name);\r\n return position ? dpeWriteNodePosition(node, position) : node;\r\n });\r\n next = this.withCollection(next, collection, updated);\r\n }\r\n return next;\r\n }\r\n\r\n /**\r\n * Sostituisce una collezione producendo una nuova definizione. Il cast e' confinato qui:\r\n * TypeScript non correla la chiave di collezione con il tipo di nodo dentro un accesso\r\n * dinamico, e la mappa dei tipi serve ai chiamanti, non a questa riga.\r\n */\r\n private withCollection(\r\n definition: DpeDefinition,\r\n collection: DpeNodeCollectionName,\r\n nodes: readonly unknown[],\r\n ): DpeDefinition {\r\n return { ...definition, [collection]: nodes.slice() } as DpeDefinition;\r\n }\r\n\r\n private commit(next: DpeDefinition): void {\r\n const snapshot = JSON.stringify(this._definition());\r\n const past = [...this._past(), snapshot];\r\n this._past.set(past.length > HISTORY_LIMIT ? past.slice(past.length - HISTORY_LIMIT) : past);\r\n // Una nuova mutazione chiude il ramo di redo: tenerlo sarebbe una cronologia ad albero,\r\n // che nessun comando dell'editor sa percorrere.\r\n this._future.set([]);\r\n this._definition.set(next);\r\n }\r\n}\r\n","/**\r\n * Lo stato del **catalogo** delle definizioni (ADR 0019): elenco, esiti delle chiamate, comandi.\r\n *\r\n * `@Injectable()` **senza `providedIn`**, come gli altri store: e' un provider del componente\r\n * radice della schermata di catalogo, non della root dell'applicazione. Due cataloghi in due\r\n * punti dell'ospite sono due elenchi indipendenti.\r\n *\r\n * Sta in `core/` e **non conosce il DOM**: e' il posto in cui un difetto e' silenzioso — una\r\n * chiamata mancata, un `refresh` che non parte dopo una scrittura, un errore assorbito — e quindi\r\n * il posto che ha bisogno di essere testabile senza browser.\r\n *\r\n * Cosa questo store **non** fa, e non e' una dimenticanza:\r\n *\r\n * - **non riordina** i sommari: l'ordine e' quello che l'ospite ha restituito (`contracts.md` §7),\r\n * e riordinarlo lato client combatterebbe una scelta che l'ospite ha gia' fatto;\r\n * - **non filtra**: il filtro di testo e' locale al componente, sui sommari gia' ricevuti;\r\n * - **non calcola** `nodeCount` ne' `hasErrors`: sono fatti riportati dall'ospite, e dedurli qui\r\n * sarebbe una validazione fatta dal client per interposta persona;\r\n * - **non carica documenti**: aprire e' dell'ospite, che potrebbe instradare o controllare\r\n * permessi. Il catalogo emette un nome.\r\n */\r\n\r\nimport { DestroyRef, Injectable, computed, inject, signal } from '@angular/core';\r\nimport { takeUntilDestroyed } from '@angular/core/rxjs-interop';\r\nimport { EMPTY, Observable, catchError, map, switchMap, tap } from 'rxjs';\r\n\r\nimport { DpeBuilderApi } from '../api/dpe-builder-api';\r\nimport { DpeApiError } from '../api/dpe-api-error';\r\nimport { DpeDefinitionSummary } from '../model/dpe-api.model';\r\nimport { SqlDialectKind } from '../model/dpe-definition.model';\r\nimport { dpeCreateCommandAvailability, dpeReadCapabilities } from './dpe-capabilities';\r\nimport { dpeEmptyDefinition } from './dpe-document.store';\r\n\r\n@Injectable()\r\nexport class DpeCatalogStore {\r\n private readonly api = inject(DpeBuilderApi);\r\n private readonly destroyRef = inject(DestroyRef);\r\n\r\n /**\r\n * Le primitive disponibili, fotografate **alla costruzione** per identita' del metodo\r\n * (`dpe-capabilities.ts`). La disponibilita' si sa prima del clic: un comando che sembra attivo\r\n * e fallisce alla pressione insegna a non fidarsi dei bottoni.\r\n */\r\n readonly capabilities = dpeReadCapabilities(this.api);\r\n\r\n /** Il comando di creazione dipende da **due** primitive, e il motivo va mostrato. */\r\n readonly createCommand = dpeCreateCommandAvailability(this.capabilities);\r\n\r\n private readonly _summaries = signal<readonly DpeDefinitionSummary[]>([]);\r\n private readonly _isLoading = signal(false);\r\n private readonly _isMutating = signal(false);\r\n private readonly _error = signal<DpeApiError | null>(null);\r\n private readonly _hasLoaded = signal(false);\r\n private readonly _environmentDialect = signal<SqlDialectKind | undefined>(undefined);\r\n\r\n /** Nell'ordine restituito dall'ospite, mai riordinato. */\r\n readonly summaries = this._summaries.asReadonly();\r\n\r\n /**\r\n * Due stati distinti e non uno: **elencare** e **scrivere** si vedono diversi. Un unico\r\n * \"occupato\" farebbe lampeggiare l'elenco a ogni cancellazione, e nasconderebbe il fatto che\r\n * dopo una scrittura l'elenco si ricarica.\r\n */\r\n readonly isLoading = this._isLoading.asReadonly();\r\n readonly isMutating = this._isMutating.asReadonly();\r\n\r\n readonly error = this._error.asReadonly();\r\n\r\n /** Vero dopo la prima risposta: prima, un elenco vuoto non significa «nessuna definizione». */\r\n readonly hasLoaded = this._hasLoaded.asReadonly();\r\n\r\n /**\r\n * Il dialetto **dichiarato dall'ambiente** (ADR 0017), che e' quello con cui nasce una\r\n * definizione creata da qui. Non si chiede all'utente e non si sceglie per ripiego: senza\r\n * dichiarazione il comando di creazione resta spento (`createCommand`).\r\n */\r\n readonly environmentDialect = this._environmentDialect.asReadonly();\r\n\r\n readonly isEmpty = computed(() => this._hasLoaded() && this._summaries().length === 0);\r\n\r\n constructor() {\r\n // L'elenco si chiede all'apertura: un catalogo che mostra righe solo dopo un clic su\r\n // «Aggiorna» e' un catalogo che sembra vuoto.\r\n this.refresh();\r\n\r\n // La dichiarazione dell'ambiente e' un fatto dell'installazione: si chiede una volta. Un\r\n // guasto qui non e' un errore del catalogo — resta il comportamento di `getEnvironment`\r\n // assente, cioe' creazione spenta.\r\n if (this.capabilities.getEnvironment) {\r\n this.api\r\n .getEnvironment()\r\n .pipe(\r\n tap((environment) => this._environmentDialect.set(environment.dialect)),\r\n catchError(() => EMPTY),\r\n takeUntilDestroyed(this.destroyRef),\r\n )\r\n .subscribe();\r\n }\r\n }\r\n\r\n /**\r\n * Ricarica l'elenco. Senza `listDefinitions` non e' un errore: e' un ambiente che non offre il\r\n * catalogo, e chi rende lo dice guardando `capabilities.listDefinitions`.\r\n */\r\n refresh(): void {\r\n if (!this.capabilities.listDefinitions) {\r\n return;\r\n }\r\n this._isLoading.set(true);\r\n this._error.set(null);\r\n this.api\r\n .listDefinitions()\r\n .pipe(\r\n tap((summaries) => {\r\n this._summaries.set(summaries);\r\n this._isLoading.set(false);\r\n this._hasLoaded.set(true);\r\n }),\r\n catchError((error: unknown) => this.fail(error, () => this._isLoading.set(false))),\r\n takeUntilDestroyed(this.destroyRef),\r\n )\r\n .subscribe();\r\n }\r\n\r\n /**\r\n * Crea una definizione **subito**, non al primo salvataggio: `save` significa «scrivi questa\r\n * definizione» e non distingue inserimento da aggiornamento, quindi solo `createDefinition` da'\r\n * all'ospite il punto in cui rifiutare un nome duplicato. Una definizione che esiste solo nel\r\n * browser e' inoltre uno stato che l'elenco non puo' mostrare.\r\n *\r\n * Il dialetto arriva dall'ambiente e non dall'utente (ADR 0017): lo passa il chiamante, che lo\r\n * legge da `environmentDialect`.\r\n *\r\n * L'osservabile e' **freddo**: chi comanda si iscrive **una volta** e riceve il sommario che\r\n * l'ospite ha restituito — non quello costruito qui, perche' il nome puo' essere stato\r\n * normalizzato.\r\n */\r\n create(name: string, label: string, dialect: SqlDialectKind): Observable<DpeDefinitionSummary> {\r\n return this.mutate(this.api.createDefinition(dpeEmptyDefinition(name, label, dialect)));\r\n }\r\n\r\n /**\r\n * Duplica: `load` **e poi** `createDefinition`. Non esiste una primitiva di duplicazione sul\r\n * contratto, e non deve esistere — si comporrebbe di queste due comunque, e una primitiva\r\n * dedicata dovrebbe ridecidere lato host cosa cambia.\r\n *\r\n * Il dialetto e' quello della **definizione di partenza**, non quello dell'ambiente: cambiarlo\r\n * duplicando sarebbe una migrazione silenziosa: il dialetto decide le capacita' con cui si\r\n * compila, e se il compilatore di formule non lo copre la copia non compila nessuna espressione\r\n * (ADR 0031 §5).\r\n */\r\n duplicate(\r\n sourceName: string,\r\n name: string,\r\n label: string,\r\n ): Observable<DpeDefinitionSummary> {\r\n return this.mutate(\r\n this.api\r\n .load(sourceName)\r\n .pipe(switchMap((source) => this.api.createDefinition({ ...source, name, label }))),\r\n );\r\n }\r\n\r\n /** Cancella per nome e ricarica l'elenco. La conferma e' della schermata, non dello store. */\r\n remove(name: string): void {\r\n if (!this.capabilities.deleteDefinition) {\r\n return;\r\n }\r\n this._isMutating.set(true);\r\n this._error.set(null);\r\n this.api\r\n .deleteDefinition(name)\r\n .pipe(\r\n tap(() => {\r\n this._isMutating.set(false);\r\n this.refresh();\r\n }),\r\n catchError((error: unknown) => this.fail(error, () => this._isMutating.set(false))),\r\n takeUntilDestroyed(this.destroyRef),\r\n )\r\n .subscribe();\r\n }\r\n\r\n dismissError(): void {\r\n this._error.set(null);\r\n }\r\n\r\n /**\r\n * Il tratto comune delle due scritture: stato di attesa, **`refresh` dopo il successo** — un\r\n * elenco che omette cio' che l'utente ha appena creato e' un elenco che mente — ed errore nel\r\n * signal invece che propagato. Chi comanda non deve trattare l'errore due volte.\r\n */\r\n private mutate(source: Observable<DpeDefinitionSummary>): Observable<DpeDefinitionSummary> {\r\n this._error.set(null);\r\n return source.pipe(\r\n /*\r\n * La risposta di una scrittura **e' un sommario**, e chi crea apre il nome che c'e' dentro\r\n * (puo' essere stato normalizzato). Se il nome non c'e', l'apertura partirebbe con\r\n * `undefined` e il difetto ricomparirebbe due schermate dopo: qui diventa subito un errore\r\n * nel banner del catalogo, che nomina la causa.\r\n */\r\n map((summary) => {\r\n if (typeof summary?.name !== 'string' || summary.name.trim().length === 0) {\r\n throw new DpeApiError(\r\n 'Backend',\r\n \"La scrittura non ha restituito un sommario con `name`: senza il nome non c'e' niente \" +\r\n 'da aprire. Un backend che risponde `204`, che restituisce il documento invece del ' +\r\n 'sommario, o che risponde in PascalCase produce esattamente questo.',\r\n summary,\r\n );\r\n }\r\n return summary;\r\n }),\r\n tap({\r\n subscribe: () => this._isMutating.set(true),\r\n next: () => {\r\n this._isMutating.set(false);\r\n this.refresh();\r\n },\r\n }),\r\n catchError((error: unknown) => this.fail(error, () => this._isMutating.set(false))),\r\n );\r\n }\r\n\r\n /** Un errore del provider finisce in `error()` e **non lancia**: nessuno lo raccoglierebbe. */\r\n private fail(error: unknown, reset: () => void): Observable<never> {\r\n reset();\r\n this._error.set(this.toApiError(error));\r\n return EMPTY;\r\n }\r\n\r\n /** Cio' che l'host non categorizza diventa `Transport`, la categoria \"riprovabile\". */\r\n private toApiError(error: unknown): DpeApiError {\r\n if (DpeApiError.is(error)) {\r\n return error;\r\n }\r\n const message = error instanceof Error ? error.message : 'Errore inatteso dal backend.';\r\n return new DpeApiError('Transport', message, error);\r\n }\r\n}\r\n","/**\r\n * Le condizioni dei campi condizionali, **in un punto solo**.\r\n *\r\n * Un campo condizionale e' un campo del modello che ha significato solo quando un altro campo del\r\n * **suo stesso nodo** — il discriminante — vale una certa cosa: `jsonOptions` solo con\r\n * `target === 'Json'`, `expressionFields` solo con `kind` diverso da `Slice`, e cosi' via. Le\r\n * condizioni sono usate dall'ispettore in due direzioni opposte:\r\n *\r\n * 1. **resa** (`frontend.md` §5-ter, normativa): il controllo si rende quando la condizione vale\r\n * **oppure** quando il campo porta un valore. Un valore fuori condizione che nessun controllo\r\n * mostra non e' cancellabile dall'editor, e per **I7** sopravvive a ogni salvataggio;\r\n * 2. **ripulitura**: cambiare il discriminante e' un gesto deliberato con cui l'autore dichiara\r\n * inapplicabili i campi che il valore nuovo non governa, e quei campi escono dal documento.\r\n *\r\n * Fino a LS9 le stesse condizioni erano scritte **due volte in ogni ispettore**, una per direzione,\r\n * a pochi metodi di distanza. E' la duplicazione piu' facile da introdurre e la piu' difficile da\r\n * vedere, perche' i sintomi di una divergenza sono silenziosi e nessun test li coglie:\r\n *\r\n * - un campo **ripulito che resta mostrato** — la ripulitura lo toglie, la resa lo rimette, e\r\n * l'utente vede un controllo che non corrisponde a niente nel documento;\r\n * - §5-ter che **smette di coprire un campo** — la resa lo nasconde mentre il documento lo porta,\r\n * cioe' il difetto che quella regola esiste per chiudere, tornato in silenzio.\r\n *\r\n * Con la tabella qui sotto le due direzioni leggono **la stessa riga**: `dpeFieldVisibility()`\r\n * costruisce un segnale per ogni voce, `dpePruneInapplicable()` scorre le voci una per una.\r\n * Aggiungere un campo condizionale domani significa scrivere **una** riga; scriverla per la resa e\r\n * dimenticarla per la ripulitura non e' possibile, perche' e' la stessa riga.\r\n *\r\n * ## Cio' che questo file NON chiude, e va detto\r\n *\r\n * **La fonte autorevole di queste condizioni e' il backend**, e resta ricopiata a mano:\r\n * `dpe/dpe/Validation/ModelRules.cs` e `dpe/dpe/Validation/WritebackRules.cs`. Qui resta una\r\n * **regola di dominio in TypeScript**, cioe' una deroga dichiarata a un vincolo del progetto\r\n * (`frontend.md` §5-ter, «duplicazione dichiarata e temporanea»). `npm run check:modelli` **non**\r\n * la copre: confronta le forme dei tipi, non le condizioni.\r\n *\r\n * Quindi: chi tocca una condizione di la' deve toccarla anche di qua, e i due sintomi silenziosi\r\n * elencati sopra valgono identici per una divergenza dal backend — con l'aggravante che li' non\r\n * c'e' nemmeno il compilatore. La direzione che chiude la classe invece di sorvegliarla (la\r\n * tabella come **dato** pubblicato dal contratto, letta da `ModelRules`) e' la questione aperta 32.\r\n *\r\n * ## Cosa non sta in tabella, e perche'\r\n *\r\n * Solo i campi **del nodo**: la ripulitura toglie chiavi dal nodo, quindi una voce deve essere una\r\n * chiave del nodo. Una condizione che governa un campo **dentro una riga di una collezione** (il\r\n * tipo dichiarato di un campo di sorgente) non e' rappresentabile qui e resta scritta nel suo\r\n * ispettore, dove la meta' «porta un valore» e' un `some(...)` sulle righe.\r\n */\r\n\r\nimport { Signal, computed } from '@angular/core';\r\n\r\nimport {\r\n DataSourceNode,\r\n FilterNode,\r\n HierarchyPathNode,\r\n JoinNode,\r\n TransformNode,\r\n WritebackNode,\r\n} from '../model/dpe-definition.model';\r\n\r\n/** La condizione di applicabilita' di un campo, valutata sul nodo intero. */\r\nexport type DpeFieldCondition<TNode> = (node: TNode) => boolean;\r\n\r\n/**\r\n * La tabella di un tipo di nodo: **chiave del nodo → condizione**.\r\n *\r\n * Le chiavi sono limitate a `keyof TNode` cosi' un campo rinominato nel modello non lascia qui una\r\n * riga orfana che nessuno consuma, e la ripulitura non puo' togliere una chiave inventata.\r\n */\r\nexport type DpeConditionalFields<TNode> = {\r\n readonly [K in keyof TNode]?: DpeFieldCondition<TNode>;\r\n};\r\n\r\n/** Un segnale di visibilita' per ogni voce della tabella, con lo stesso nome del campo. */\r\nexport type DpeFieldVisibility<TConditions> = {\r\n readonly [K in keyof TConditions]: Signal<boolean>;\r\n};\r\n\r\n/**\r\n * «Il campo porta un valore», nel senso di `frontend.md` §5-ter.\r\n *\r\n * Nessuna delle equivalenze qui sotto e' un giudizio sul valore — dire che e' sbagliato tocca al\r\n * backend, e la regola di resa vale **diagnostica o no**:\r\n *\r\n * - **stringa vuota = assente**, perche' il backend confronta con `string.IsNullOrEmpty`: un\r\n * `processingGroupFieldName: ''` arrivato da fuori non deve far comparire un campo vuoto, che\r\n * sarebbe il rumore che la regola promette di non introdurre;\r\n * - **`false` = assente**, perche' i campi governati sono `bool` **non nullable** con default\r\n * `false` lato C# (`OnlyChangedRows`, `AllowsMultipleLookupValues`): dopo la deserializzazione\r\n * `false` e' indistinguibile dall'assenza;\r\n * - **collezione vuota = assente**: un `criteria: []` non dichiara niente, e il backend lo conta\r\n * con `Count(...) == 0`.\r\n *\r\n * Un oggetto invece **e' sempre** una dichiarazione, anche `{}`: per il backend un `jsonOptions`\r\n * non nullo fuori dal proprio target e' `DPE_MODEL_CONDITIONAL_FORBIDDEN` qualunque cosa contenga.\r\n */\r\nexport function dpeDeclares(value: unknown): boolean {\r\n if (value === undefined || value === null || value === '' || value === false) {\r\n return false;\r\n }\r\n return Array.isArray(value) ? value.length > 0 : true;\r\n}\r\n\r\n/**\r\n * I segnali di resa di §5-ter: **la condizione vale oppure il campo porta un valore**.\r\n *\r\n * Si costruiscono dalla tabella intera e non uno per uno, cosi' il segnale di un campo nuovo esiste\r\n * per il solo fatto che la sua riga esiste. Nel template il tipo fa il resto: `shows.jsonOptions()`\r\n * su una voce che non c'e' non compila, quindi non si puo' nascondere un campo per distrazione.\r\n */\r\nexport function dpeFieldVisibility<TNode extends object, TConditions extends DpeConditionalFields<TNode>>(\r\n node: Signal<TNode>,\r\n conditions: TConditions,\r\n): DpeFieldVisibility<TConditions> {\r\n const visibility: Record<string, Signal<boolean>> = {};\r\n for (const [field, condition] of Object.entries(conditions) as [string, DpeFieldCondition<TNode>][]) {\r\n visibility[field] = computed(() => {\r\n const current = node();\r\n return condition(current) || dpeDeclares((current as Record<string, unknown>)[field]);\r\n });\r\n }\r\n return visibility as DpeFieldVisibility<TConditions>;\r\n}\r\n\r\n/**\r\n * La modifica di un discriminante, **piu'** la rimozione dei campi che con il valore nuovo non si\r\n * applicano piu'.\r\n *\r\n * La condizione si valuta sul nodo **come sara' dopo il gesto** (`{ ...node, ...change }`), non su\r\n * quello di adesso: e' il solo modo di trattare un discriminante che ne cambia un altro nello\r\n * stesso gesto — su un writeback la destinazione decide l'operazione, e l'operazione decide il\r\n * campo di identificazione esterna.\r\n *\r\n * E si ripete **finche' non si stabilizza**, perche' una rimozione puo' invalidare la condizione\r\n * di un altro campo: portando una scrittura `Upsert` da `Database` a `Json`, il primo giro toglie\r\n * `operation` e solo il secondo si accorge che `externalIdFieldName` non e' piu' governato da\r\n * niente. Con un giro solo quel valore restava nel documento su un artefatto, dove il backend\r\n * **non lo diagnostica**: ignorato e invisibile, il caso peggiore. Trovato **eseguendo** la demo,\r\n * non leggendo il codice — la versione a un giro passava tutti i test che c'erano, e il test che\r\n * la coglie e' stato scritto dopo averla vista fallire a schermo.\r\n *\r\n * Il punto fisso esiste sempre: ogni giro puo' solo **aggiungere** rimozioni, i campi sono finiti\r\n * e un campo tolto non torna. Una condizione scritta al contrario (vera **perche'** l'altro campo\r\n * e' assente) renderebbe il risultato dipendente dall'ordine: in tabella non ce ne sono, e questo\r\n * e' il motivo per cui non ce ne devono essere.\r\n *\r\n * **Si toglie cio' che non si applica, non cio' che apparteneva al valore precedente**, e la\r\n * differenza non e' teorica: `Expression` → `ComputeRelative` non tocca `expressionFields`, perche'\r\n * quel campo e' governato da entrambi. Un «cambiando tipo si azzera» sarebbe piu' semplice da\r\n * scrivere e cancellerebbe espressioni SQL su un passaggio che non le rende inapplicabili.\r\n *\r\n * Togliere e' **rimuovere la chiave** (`undefined`), non scriverci `false` o `[]`: le due forme\r\n * sono equivalenti per il validatore e diverse per il documento, e il documento e' cio' che si\r\n * salva (**I7**).\r\n *\r\n * Si rimuove **solo cio' che c'e'**: un campo gia' assente non entra nel patch. Sul documento\r\n * salvato non cambia niente — una chiave a `undefined` sparisce serializzando — ma il patch\r\n * diventa il resoconto esatto del gesto, e «questo cambio non tocca niente» diventa una cosa\r\n * osservabile invece di una cosa da credere.\r\n *\r\n * Non e' distruttivo perche' il patch passa da `DpeDocumentStore.updateNode` → `commit`, che mette\r\n * l'istantanea precedente nella cronologia: «Annulla» rimette tutto. Verificato a schermo in\r\n * LS7-quater, non dedotto — era il solo fatto che avrebbe potuto togliere la ragione a questo\r\n * comportamento.\r\n */\r\nexport function dpePruneInapplicable<TNode extends object, TConditions extends DpeConditionalFields<TNode>>(\r\n node: TNode,\r\n change: Partial<TNode>,\r\n conditions: TConditions,\r\n): Partial<TNode> {\r\n const entries = Object.entries(conditions) as [string, DpeFieldCondition<TNode>][];\r\n const next = { ...node, ...change } as Record<string, unknown>;\r\n const patch: Record<string, unknown> = { ...change };\r\n let removedSomething = true;\r\n while (removedSomething) {\r\n removedSomething = false;\r\n for (const [field, condition] of entries) {\r\n // `!== undefined` e non `dpeDeclares(...)`: un `onlyChangedRows: false` o un `criteria: []`\r\n // sono chiavi **presenti** nel documento, e vanno tolte anche se non dichiarano niente. E'\r\n // anche la guardia che chiude il giro: un campo gia' tolto non si ritoglie.\r\n if (next[field] === undefined || condition(next as TNode)) {\r\n continue;\r\n }\r\n next[field] = undefined;\r\n patch[field] = undefined;\r\n removedSomething = true;\r\n }\r\n }\r\n return patch as Partial<TNode>;\r\n}\r\n\r\n/**\r\n * Sorgente: il tipo decide da dove si legge (`ModelRules.cs`, `ApplyDataSources`).\r\n *\r\n * Nessuno dei tre e' diagnosticato **fuori** condizione — il backend li richiede dove servono e\r\n * ignora il resto — e non cambia niente: §5-ter guarda il documento, non l'elenco dei codici.\r\n */\r\nexport const DPE_DATA_SOURCE_CONDITIONS = {\r\n tableName: (node) => node.kind === 'Table',\r\n csvOptions: (node) => node.kind === 'Csv',\r\n externalDatasetName: (node) => node.kind === 'ExternalDataset',\r\n} satisfies DpeConditionalFields<DataSourceNode>;\r\n\r\n/**\r\n * Filtro: `isDynamic` sceglie quale meta' del nodo vale (`ModelRules.cs`, `ApplyFilters`).\r\n *\r\n * Il parametro serve al filtro dinamico; criteri e condizione a quello statico.\r\n */\r\nexport const DPE_FILTER_CONDITIONS = {\r\n criteria: (node) => !node.isDynamic,\r\n condition: (node) => !node.isDynamic,\r\n filterParameterName: (node) => !!node.isDynamic,\r\n} satisfies DpeConditionalFields<FilterNode>;\r\n\r\n/**\r\n * Giunzione: la molteplicita' ammessa ha significato solo su una `Lookup`\r\n * (`JoinRules.cs`, `DialectRules.cs`).\r\n */\r\nexport const DPE_JOIN_CONDITIONS = {\r\n allowsMultipleLookupValues: (node) => node.kind === 'Lookup',\r\n} satisfies DpeConditionalFields<JoinNode>;\r\n\r\n/** Percorso gerarchico: le aggregazioni servono quando sono dichiarate richieste. */\r\nexport const DPE_HIERARCHY_PATH_CONDITIONS = {\r\n aggregateFields: (node) => !!node.isAggregationRequired,\r\n} satisfies DpeConditionalFields<HierarchyPathNode>;\r\n\r\n/**\r\n * Trasformazione: `kind` governa i quattro campi (`ModelRules.cs`, `ApplyTransforms`).\r\n *\r\n * `expressionFields` e' governato da **due** tipi, ed e' il motivo per cui la ripulitura non puo'\r\n * essere un azzeramento.\r\n */\r\nexport const DPE_TRANSFORM_CONDITIONS = {\r\n expressionFields: (node) => node.kind !== 'Slice',\r\n droppedFields: (node) => node.kind === 'Slice',\r\n partitionBy: (node) => node.kind === 'ComputeRelative',\r\n orderBy: (node) => node.kind === 'ComputeRelative',\r\n} satisfies DpeConditionalFields<TransformNode>;\r\n\r\n/**\r\n * Scrittura: `target` governa sei campi, **`operation` ne governa un settimo**\r\n * (`ModelRules.cs`, `ApplyWritebacks`; `WritebackRules.cs`, `CheckArtifactOptions`,\r\n * `CheckProcessingGroupField`, `CheckFailureHandling`).\r\n *\r\n * `tableName` e `onlyChangedRows` sono in tabella pur **non** essendo diagnosticati fuori da\r\n * `Database`: nessuna regola li vieta, quindi il valore non e' «invalido», e' **ignorato e\r\n * invisibile** — che e' peggio, perche' niente lo segnala e un cambio di destinazione lo puo'\r\n * rendere attivo di colpo. Far dipendere questa tabella dall'elenco dei codici diagnostici\r\n * accoppierebbe l'ispettore a un'altra regola di dominio ancora.\r\n *\r\n * `externalIdFieldName` dipende da `operation`, **non** da `target`: e' la condizione che il\r\n * backend applica (`Operation == Upsert`, richiesto li' e vietato altrove) ed e' la sola scritta\r\n * qui. Fino a LS9 la ripulitura ne usava una seconda, piu' larga (`Target == Database`): era\r\n * proprio la divergenza interna che questo file esiste per rendere impossibile.\r\n *\r\n * `failureHandling` e' vietato dal backend anche dentro un gruppo atomico, e qui non si dice: la\r\n * condizione di resa piu' larga fa comparire il controllo — con il rilievo sotto — invece di\r\n * nascondere un valore che l'autore dovrebbe poter togliere. Restringerla farebbe sparire il\r\n * controllo proprio dove serve.\r\n */\r\nexport const DPE_WRITEBACK_CONDITIONS = {\r\n tableName: (node) => node.target === 'Database',\r\n operation: (node) => node.target === 'Database',\r\n externalIdFieldName: (node) => node.operation === 'Upsert',\r\n onlyChangedRows: (node) => node.target === 'Database',\r\n processingGroupFieldName: (node) => node.target === 'Database',\r\n failureHandling: (node) => node.target === 'Database',\r\n jsonOptions: (node) => node.target === 'Json',\r\n csvOptions: (node) => node.target === 'Csv',\r\n} satisfies DpeConditionalFields<WritebackNode>;\r\n","/**\r\n * La lettura dei **tre esiti** del catalogo delle formule — `contracts.md` §7, normativo.\r\n *\r\n * Sta in una funzione pura e non in un `if` dentro un componente per la ragione che ha reso questo\r\n * lotto necessario: la versione precedente del contratto consegnava il catalogo **nudo**, quindi i\r\n * tre esiti del service ne diventavano due, e *dialetto non coperto* e *backend rotto* arrivavano\r\n * a schermo identici. Un degrado silenzioso non si vede rileggendo un componente; si vede\r\n * scrivendo una tabella e provandola.\r\n *\r\n * **Il discriminante e' `diagnostics`, non `catalog`.** E' la riga che vale piu' di tutte le\r\n * altre, perche' la stesura precedente diceva il contrario: con `ProvidesCatalog == false` il\r\n * catalogo arriva **presente e vuoto**, con il nome del linguaggio e zero diagnostiche — quindi\r\n * un editor che ramasse su `catalog == null` non entrerebbe mai in quel caso, e mostrerebbe un\r\n * fallimento dove non c'e' niente da segnalare.\r\n *\r\n * `catalog` nullo o assente significa **una cosa sola: non e' stato prodotto**.\r\n */\r\n\r\nimport {\r\n DpeExpressionForm,\r\n DpeFormulaCatalog,\r\n DpeFormulaCatalogResult,\r\n} from '../model/dpe-api.model';\r\nimport { DPE_CATALOG_UNAVAILABLE, Diagnostic } from '../model/dpe-diagnostics.model';\r\n\r\n/**\r\n * I quattro stati in cui il campo formula puo' trovarsi rispetto al catalogo — tre del contratto\r\n * piu' «non lo so ancora», che e' quello in cui il campo nasce.\r\n *\r\n * Unione discriminata e non un oggetto con quattro campi opzionali: cosi' «fallito **e** con\r\n * tavolozza» non e' rappresentabile, e chi rende non deve decidere quale dei due vince.\r\n */\r\nexport type DpeFormulaCatalogOutcome =\r\n /** Nessuna risposta conservata: non e' stato chiesto, e' in volo, o la richiesta e' fallita. */\r\n | { readonly kind: 'Unknown' }\r\n /** Catalogo consegnato **con contenuto**: si mostra la tavolozza. */\r\n | { readonly kind: 'Palette'; readonly catalog: DpeFormulaCatalog }\r\n /**\r\n * Catalogo consegnato **vuoto** e nessuna diagnostica: il compilatore non descrive il proprio\r\n * linguaggio, che non e' un errore. Niente tavolozza e **nessun rumore**; `languageName` c'e' ed\r\n * e' l'unica cosa da mostrare — e l'etichetta del campo la prende comunque dall'ambiente.\r\n */\r\n | { readonly kind: 'Silent'; readonly languageName?: string }\r\n /**\r\n * Diagnostiche non vuote: il catalogo non e' stato prodotto e **il perche' si mostra**.\r\n *\r\n * `lead` e' la riga in testa, `details` tutto il resto **nell'ordine ricevuto**. La separazione\r\n * non e' estetica: il sacco strutturale riguarda l'intera definizione e puo' contenere voci che\r\n * con il nodo richiesto non c'entrano, filtrarle per pertinenza non e' affidabile (l'errore che\r\n * spiega lo schema puo' stare a piu' salti di distanza), quindi il rimedio dichiarato e' una\r\n * riga in testa e il resto come dettaglio.\r\n */\r\n | {\r\n readonly kind: 'Failed';\r\n readonly lead: Diagnostic;\r\n readonly details: readonly Diagnostic[];\r\n readonly hasError: boolean;\r\n };\r\n\r\n/**\r\n * Legge l'involucro e dice in quale dei quattro stati ci si trova.\r\n *\r\n * L'ordine dei controlli e' il contratto messo in fila, e non e' intercambiabile:\r\n *\r\n * 1. nessun risultato -> `Unknown`;\r\n * 2. **diagnostiche non vuote** -> `Failed`, indipendentemente da `catalog`;\r\n * 3. catalogo con contenuto -> `Palette`;\r\n * 4. tutto il resto -> `Silent`, cioe' niente tavolozza e nessun rumore.\r\n *\r\n * Il quarto ramo assorbe anche il caso fuori contratto «nessun catalogo e nessuna diagnostica»:\r\n * a schermo e' indistinguibile dal terzo esito — niente da mostrare e niente da segnalare — e\r\n * inventare un quinto stato per rimproverare il backend darebbe all'utente un messaggio su cui non\r\n * puo' agire.\r\n */\r\nexport function dpeFormulaCatalogOutcome(\r\n result: DpeFormulaCatalogResult | undefined,\r\n): DpeFormulaCatalogOutcome {\r\n if (!result) {\r\n return { kind: 'Unknown' };\r\n }\r\n const diagnostics = result.diagnostics ?? [];\r\n if (diagnostics.length > 0) {\r\n /*\r\n * `DPE_CATALOG_UNAVAILABLE` va **in testa** quando c'e': nomina la sorgente su cui agire. Se\r\n * non c'e' — il ramo del dialetto non coperto, per esempio — la testa e' la prima diagnostica\r\n * ricevuta, perche' un blocco d'errore senza una prima riga costringerebbe a leggerle tutte\r\n * per capire di cosa si parla.\r\n */\r\n const leadIndex = Math.max(\r\n 0,\r\n diagnostics.findIndex((entry) => entry.code === DPE_CATALOG_UNAVAILABLE),\r\n );\r\n return {\r\n kind: 'Failed',\r\n lead: diagnostics[leadIndex],\r\n details: diagnostics.filter((_, index) => index !== leadIndex),\r\n hasError: diagnostics.some((entry) => entry.severity === 'Error'),\r\n };\r\n }\r\n const catalog = result.catalog;\r\n if (catalog && dpeFormulaCatalogSize(catalog) > 0) {\r\n return { kind: 'Palette', catalog };\r\n }\r\n return { kind: 'Silent', languageName: catalog?.languageName };\r\n}\r\n\r\n/**\r\n * L'etichetta con cui si nomina all'utente la forma pretesa dal campo.\r\n *\r\n * E' **traduzione**, non deduzione: la forma arriva dal backend, qui si sceglie solo come\r\n * chiamarla in italiano. Dedurla dal tipo di nodo sarebbe la regola di dominio che il contratto\r\n * vieta, e la differenza fra le due cose e' tutta in questa riga.\r\n */\r\nexport function dpeExpressionFormLabel(form: DpeExpressionForm | null | undefined): string | undefined {\r\n switch (form) {\r\n case 'Value':\r\n return 'valore';\r\n case 'Predicate':\r\n return 'predicato';\r\n case 'WindowFunction':\r\n return 'funzione di finestra';\r\n default:\r\n return undefined;\r\n }\r\n}\r\n\r\n/**\r\n * La stessa cosa detta all'utente: «serve un valore», «serve una funzione di finestra».\r\n *\r\n * Esiste separata dall'etichetta nuda per una ragione banale e reale: **l'articolo dipende dal\r\n * genere**, e comporre «serve un » + etichetta nel template produce «serve un funzione di\r\n * finestra». Il template non puo' sapere il genere di una parola, questa funzione si'.\r\n */\r\nexport function dpeExpressionFormPrompt(\r\n form: DpeExpressionForm | null | undefined,\r\n): string | undefined {\r\n switch (form) {\r\n case 'Value':\r\n return 'serve un valore';\r\n case 'Predicate':\r\n return 'serve un predicato';\r\n case 'WindowFunction':\r\n return 'serve una funzione di finestra';\r\n default:\r\n return undefined;\r\n }\r\n}\r\n\r\n/**\r\n * L'incrocio fra **la forma richiesta dal nodo** e **le forme che il compilatore sa compilare**.\r\n *\r\n * E' la meta' «disabilitare» della funzione che il contratto prescriveva: la meta' «mostrare» —\r\n * la pastiglia «serve una funzione di finestra» — era implementabile da LF6, perche' il primo\r\n * operando arriva con il catalogo; il secondo non stava sul filo, e senza di esso l'editor poteva\r\n * solo dire cosa serve, mai che non si potra' avere.\r\n *\r\n * Stessa forma di `DpeCommandAvailability` (`dpe-capabilities.ts`) e per la stessa ragione: un\r\n * campo spento che non dice perche' manda a cercare un guasto che non c'e'.\r\n */\r\nexport interface DpeExpressionFormSupport {\r\n readonly isSupported: boolean;\r\n /** Presente **solo** quando la forma non e' supportata. E' il testo che il campo mostra. */\r\n readonly reason?: string;\r\n}\r\n\r\n/** Il campo e' utilizzabile: l'assenza di informazioni cade qui, e non nell'altro ramo. */\r\nconst SUPPORTATA: DpeExpressionFormSupport = { isSupported: true };\r\n\r\n/**\r\n * La frase intera per ciascuna forma, e non «non sa compilare un » piu' l'etichetta.\r\n *\r\n * `frontend.md` §5-quinquies, normativo, e costato una volta: comporre un frammento fisso con un\r\n * nome che varia in genere produce «serve un funzione di finestra». L'italiano concorda, quindi\r\n * dove serve una frase la si scrive per intero.\r\n */\r\nconst FRASE_NON_SUPPORTATA: Record<DpeExpressionForm, string> = {\r\n Value: 'Campo disabilitato: il compilatore registrato non sa compilare un valore, che e’ la forma richiesta da questo nodo.',\r\n Predicate:\r\n 'Campo disabilitato: il compilatore registrato non sa compilare un predicato, che e’ la forma richiesta da questo nodo.',\r\n WindowFunction:\r\n 'Campo disabilitato: il compilatore registrato non sa compilare una funzione di finestra, che e’ la forma richiesta da questo nodo.',\r\n};\r\n\r\n/**\r\n * Decide se il campo formula si disabilita, e con quale motivo.\r\n *\r\n * Le tre regole, in quest'ordine e non intercambiabili:\r\n *\r\n * 1. **forma richiesta assente** -> supportato: il nodo non ospita formule, o la prima validazione\r\n * non e' ancora arrivata. Non c'e' niente da incrociare;\r\n * 2. **forme supportate non dichiarate** (`undefined` o `null`) -> supportato. E' la politica del\r\n * contratto: assente significa **non si disabilita niente**. Un ospite che non dichiara le\r\n * capacita' del proprio compilatore non autorizza l'editor a indovinarle, e un campo spento per\r\n * ignoranza toglie all'utente l'unica cosa che avrebbe: provare e leggere la risposta del\r\n * backend;\r\n * 3. **dichiarate e la forma non c'e'** -> non supportato. Un elenco **vuoto** entra qui, e ci\r\n * entra di proposito: vuoto non e' assente, e' un compilatore che dichiara di non compilare\r\n * nessuna forma.\r\n *\r\n * Nessuna di queste e' una regola di dominio: la forma la dice il backend, le capacita' pure.\r\n * Questa funzione fa solo l'intersezione e sceglie le parole italiane.\r\n */\r\nexport function dpeExpressionFormSupport(\r\n requiredForm: DpeExpressionForm | null | undefined,\r\n supportedForms: readonly DpeExpressionForm[] | null | undefined,\r\n): DpeExpressionFormSupport {\r\n if (!requiredForm || supportedForms == null) {\r\n return SUPPORTATA;\r\n }\r\n if (supportedForms.includes(requiredForm)) {\r\n return SUPPORTATA;\r\n }\r\n const supportate = supportedForms\r\n .map((form) => dpeExpressionFormLabel(form))\r\n .filter((label): label is string => label !== undefined);\r\n // L'elenco delle forme che restano: i nomi nudi qui stanno bene, sono un elenco e non una frase.\r\n const coda =\r\n supportate.length > 0\r\n ? ` Forme supportate: ${supportate.join(', ')}.`\r\n : ' Il compilatore non dichiara nessuna forma supportata.';\r\n return { isSupported: false, reason: FRASE_NON_SUPPORTATA[requiredForm] + coda };\r\n}\r\n\r\n/** Quante voci porta un catalogo. Zero e' l'esito «il compilatore non si descrive», non un guasto. */\r\nexport function dpeFormulaCatalogSize(catalog: DpeFormulaCatalog): number {\r\n return (\r\n (catalog.functions?.length ?? 0) +\r\n (catalog.operators?.length ?? 0) +\r\n (catalog.symbols?.length ?? 0)\r\n );\r\n}\r\n","/**\r\n * Il ponte fra il documento e il backend: valida in modo **debounced**, chiede l'anteprima SQL su\r\n * comando e salva su comando.\r\n *\r\n * Il salvataggio sta qui e non in un componente per la stessa ragione degli altri due: nessun\r\n * componente conosce il trasporto. Che il nome della classe sia piu' stretto della sua\r\n * responsabilita' e' un debito segnalato all'architect, non una svista.\r\n *\r\n * Perche' il debounce non e' un dettaglio: la chiamata al service DPE e' sincrona nel\r\n * backend, cioe' occupa il chiamante finche' non termina. Incatenarne una a ogni battitura\r\n * significa una coda di lavori che nessuno leggera' — e nel frattempo l'editor mostra un\r\n * risultato vecchio. Qui si aspetta la pausa di digitazione, si annulla la richiesta in volo\r\n * quando ne parte una nuova (`switchMap`), e si mostra sempre lo stato di attesa.\r\n *\r\n * L'editing resta **sincrono** mentre una validazione e' in volo: il documento e' la fonte di\r\n * verita', le diagnostiche arrivano dopo e non bloccano niente.\r\n *\r\n * Provider del componente radice come gli store: montare due editor non deve significare due\r\n * servizi che validano lo stesso documento.\r\n */\r\n\r\nimport { DestroyRef, Injectable, computed, inject, signal } from '@angular/core';\r\nimport { toObservable, takeUntilDestroyed } from '@angular/core/rxjs-interop';\r\nimport { EMPTY, catchError, debounceTime, filter, of, switchMap, tap } from 'rxjs';\r\n\r\nimport { DpeBuilderApi } from '../api/dpe-builder-api';\r\nimport { DpeApiError } from '../api/dpe-api-error';\r\nimport { DpeExpressionForm, DpeParameterValue } from '../model/dpe-api.model';\r\nimport { SqlDialectKind } from '../model/dpe-definition.model';\r\nimport { dpeReadCapabilities } from './dpe-capabilities';\r\nimport { DpeDocumentStore } from './dpe-document.store';\r\n\r\n/** Pausa di digitazione oltre la quale vale la pena chiedere al backend. */\r\nconst VALIDATE_DEBOUNCE_MS = 400;\r\n\r\n/**\r\n * Durata dell'avviso di salvataggio riuscito. Transitorio di proposito: il fatto durevole e'\r\n * «nessuna modifica» nell'indicatore, e un avviso di successo che resta va poi chiuso a mano.\r\n */\r\nconst SAVE_NOTICE_MS = 2600;\r\n\r\n@Injectable()\r\nexport class DpeValidationService {\r\n private readonly api = inject(DpeBuilderApi);\r\n private readonly store = inject(DpeDocumentStore);\r\n private readonly destroyRef = inject(DestroyRef);\r\n\r\n private readonly _isValidating = signal(false);\r\n private readonly _isCompiling = signal(false);\r\n private readonly _error = signal<DpeApiError | undefined>(undefined);\r\n /**\r\n * Le primitive opzionali si sanno **alla costruzione**, per identita' del metodo\r\n * (`dpe-capabilities.ts`, `contracts.md` §7). Prima si partiva da «disponibile» e si scopriva\r\n * l'assenza dal primo `MissingService`: il comando sembrava attivo e falliva alla pressione,\r\n * che e' peggio di un comando disabilitato che ne dice il motivo.\r\n */\r\n private readonly capabilities = dpeReadCapabilities(this.api);\r\n private readonly _isPreviewAvailable = signal(this.capabilities.compilePreview);\r\n private readonly _isSaving = signal(false);\r\n /** Come l'anteprima: un host senza `save` disabilita il comando, non rompe l'editor. */\r\n private readonly _isSaveAvailable = signal(this.capabilities.save);\r\n /** Esito dell'ultimo salvataggio: transitorio al successo, persistente al fallimento. */\r\n private readonly _justSaved = signal(false);\r\n private readonly _saveError = signal<DpeApiError | undefined>(undefined);\r\n /**\r\n * Il dialetto **dichiarato dall'ambiente** (ADR 0017), quando l'host espone `getEnvironment`.\r\n * Non e' il dialetto con cui si compila — quello sta nella definizione — ed e' esattamente per\r\n * questo che i due si tengono separati: il confronto e' l'unica cosa che l'editor puo' dire.\r\n */\r\n private readonly _environmentDialect = signal<SqlDialectKind | undefined>(undefined);\r\n /**\r\n * Il nome del **linguaggio di formule** dichiarato dall'ambiente (ADR 0031/0036). Serve\r\n * all'etichetta del campo espressione, e arriva da qui e non dal catalogo per una ragione\r\n * precisa: il catalogo puo' essere assente o nullo — compilatore che non descrive il proprio\r\n * linguaggio, dialetto non coperto — mentre il nome del linguaggio si sa sempre. Letto dal\r\n * catalogo, l'editor resterebbe muto proprio nei casi in cui serve.\r\n */\r\n private readonly _expressionLanguage = signal<string | undefined>(undefined);\r\n /** Il compilatore descrive il proprio linguaggio: se no, `getFormulaCatalog` non si chiama. */\r\n private readonly _providesFormulaCatalog = signal(false);\r\n /**\r\n * Le forme che il compilatore registrato sa produrre (ADR 0031), dall'ambiente. E' il **secondo\r\n * operando** dell'incrocio che disabilita un campo formula incompilabile; il primo,\r\n * `requiredForm`, e' per nodo e arriva con la **validazione**, su `DpeNodeSchemaInfo`.\r\n *\r\n * I due arrivano da due chiamate diverse, ed e' la ragione per cui questo campo sta qui: le forme\r\n * supportate sono dell'**installazione** e si chiedono una volta all'apertura, la forma richiesta\r\n * e' del **nodo** e si aggiorna a ogni validazione.\r\n *\r\n * `undefined` non e' «nessuna forma», e' «non dichiarate», e non disabilita niente: la\r\n * distinzione e' tutta la politica del contratto e per questo non si normalizza qui in un\r\n * elenco vuoto, che direbbe l'opposto.\r\n */\r\n private readonly _supportedForms = signal<readonly DpeExpressionForm[] | undefined>(undefined);\r\n\r\n readonly isValidating = this._isValidating.asReadonly();\r\n readonly isCompiling = this._isCompiling.asReadonly();\r\n readonly error = this._error.asReadonly();\r\n readonly isPreviewAvailable = this._isPreviewAvailable.asReadonly();\r\n readonly isSaving = this._isSaving.asReadonly();\r\n readonly isSaveAvailable = this._isSaveAvailable.asReadonly();\r\n readonly justSaved = this._justSaved.asReadonly();\r\n readonly saveError = this._saveError.asReadonly();\r\n readonly environmentDialect = this._environmentDialect.asReadonly();\r\n readonly expressionLanguage = this._expressionLanguage.asReadonly();\r\n readonly providesFormulaCatalog = this._providesFormulaCatalog.asReadonly();\r\n readonly supportedForms = this._supportedForms.asReadonly();\r\n\r\n /** Vero finche' non e' arrivata la prima risposta: la UI non deve dire \"nessun problema\". */\r\n readonly hasResult = computed(() => this.store.validateResult() !== undefined);\r\n\r\n constructor() {\r\n toObservable(this.store.definition)\r\n .pipe(\r\n /*\r\n * **Non si valida il segnaposto.** Lo store nasce con `dpeEmptyDefinition('', '', 'Sqlite')`\r\n * e questo servizio si iscrive alla costruzione: se il documento vero arriva dopo la\r\n * finestra di debounce — un ospite che monta l'editor mentre `load(nome)` e' in volo, cioe'\r\n * il caso normale con una rete vera — la finestra si chiudeva sul segnaposto e l'editor\r\n * validava un documento che nessuno aveva fornito. La risposta era esatta e inutile:\r\n * `DPE_MODEL_REQUIRED_MISSING` su `name`, `label` e `dataSources` piu'\r\n * `DPE_GRAPH_NO_WRITEBACK` — quattro rilievi del segnaposto, che l'utente non puo'\r\n * correggere da qui e che sembrano riguardare la sua definizione appena creata.\r\n *\r\n * Il filtro sta **prima** del debounce: il segnaposto non deve nemmeno aprire la finestra.\r\n */\r\n filter(() => this.store.hasDocument()),\r\n debounceTime(VALIDATE_DEBOUNCE_MS),\r\n tap(() => {\r\n this._isValidating.set(true);\r\n this._error.set(undefined);\r\n }),\r\n // `switchMap`: una definizione piu' recente rende inutile la risposta precedente, e\r\n // applicarla dopo mostrerebbe diagnostiche di un documento che non esiste piu'.\r\n switchMap((definition) =>\r\n this.api.validate(definition).pipe(\r\n tap((result) => {\r\n this.store.applyValidateResult(result);\r\n this._isValidating.set(false);\r\n }),\r\n catchError((error: unknown) => {\r\n this._isValidating.set(false);\r\n this._error.set(this.toApiError(error));\r\n // Le diagnostiche precedenti **restano**: cancellarle direbbe \"va tutto bene\"\r\n // proprio quando non lo sappiamo piu'.\r\n return EMPTY;\r\n }),\r\n ),\r\n ),\r\n takeUntilDestroyed(this.destroyRef),\r\n )\r\n .subscribe();\r\n\r\n // La dichiarazione dell'ambiente si chiede **una volta**, all'apertura: e' un fatto\r\n // dell'installazione, non cambia mentre si edita. Un guasto qui non si mostra come errore\r\n // dell'editor — senza dichiarazione l'editor si comporta come con `getEnvironment` assente.\r\n if (this.capabilities.getEnvironment) {\r\n this.api\r\n .getEnvironment()\r\n .pipe(\r\n tap((environment) => {\r\n this._environmentDialect.set(environment.dialect);\r\n // I due campi dell'ADR 0036 arrivano con la stessa risposta: sono fatti\r\n // dell'installazione come il dialetto, e chiederli a parte vorrebbe dire un secondo\r\n // giro di rete per sapere una cosa che si sa a tempo di configurazione.\r\n this._expressionLanguage.set(environment.expressionLanguage);\r\n this._providesFormulaCatalog.set(environment.providesFormulaCatalog === true);\r\n /*\r\n * `?? undefined` e non `?? []`: sul filo la proprieta' puo' arrivare assente **o**\r\n * nulla, e le due significano la stessa cosa — non dichiarate. Normalizzarle a un\r\n * elenco vuoto significherebbe l'opposto, cioe' «il compilatore non compila nessuna\r\n * forma», e disabiliterebbe **tutti** i campi formula di ogni ospite che non dichiara\r\n * le proprie capacita'.\r\n */\r\n this._supportedForms.set(environment.supportedForms ?? undefined);\r\n }),\r\n catchError(() => EMPTY),\r\n takeUntilDestroyed(this.destroyRef),\r\n )\r\n .subscribe();\r\n }\r\n\r\n // Il timer dell'avviso transitorio non deve sopravvivere all'editor: scriverebbe su un\r\n // signal di un componente distrutto.\r\n this.destroyRef.onDestroy(() => this.clearSaveNotice());\r\n }\r\n\r\n /** Anteprima dell'SQL. Opzionale sull'host: assente, il comando resta disabilitato. */\r\n requestPreview(parameterValues?: DpeParameterValue[]): void {\r\n if (!this._isPreviewAvailable()) {\r\n return;\r\n }\r\n this._isCompiling.set(true);\r\n this.api\r\n .compilePreview(this.store.definition(), parameterValues)\r\n .pipe(\r\n tap((result) => {\r\n this.store.applyCompileResult(result);\r\n this._isCompiling.set(false);\r\n }),\r\n catchError((error: unknown) => {\r\n this._isCompiling.set(false);\r\n const apiError = this.toApiError(error);\r\n if (apiError.category === 'MissingService') {\r\n this._isPreviewAvailable.set(false);\r\n } else {\r\n this._error.set(apiError);\r\n }\r\n return of(undefined);\r\n }),\r\n takeUntilDestroyed(this.destroyRef),\r\n )\r\n .subscribe();\r\n }\r\n\r\n /**\r\n * Salvataggio attraverso `DpeBuilderApi.save` (opzionale, `contracts.md` §7). Al successo\r\n * **l'editor dichiara da se'** lo stato salvato: quando l'ospite espone il metodo, e' questo il\r\n * punto che sa se e' andato a buon fine.\r\n *\r\n * Si passa a `markSaved` **il documento inviato**, non quello corrente: se l'utente scrive\r\n * mentre il salvataggio e' in volo, quelle modifiche non sono state salvate e l'indicatore deve\r\n * continuare a dirlo.\r\n */\r\n save(): void {\r\n if (!this._isSaveAvailable() || this._isSaving()) {\r\n return;\r\n }\r\n const sent = this.store.definition();\r\n this._isSaving.set(true);\r\n this._saveError.set(undefined);\r\n this.api\r\n .save(sent)\r\n .pipe(\r\n tap(() => {\r\n this._isSaving.set(false);\r\n // `markSaved` **solo** qui: un salvataggio fallito che azzerasse l'indicatore\r\n // cancellerebbe la traccia di un lavoro che non e' stato scritto da nessuna parte.\r\n this.store.markSaved(sent);\r\n this.showSaveNotice();\r\n }),\r\n catchError((error: unknown) => {\r\n this._isSaving.set(false);\r\n const apiError = this.toApiError(error);\r\n if (apiError.category === 'MissingService') {\r\n // Non dovrebbe accadere: la disponibilita' e' nota prima del clic. Se accade, l'host\r\n // ha sovrascritto la primitiva per rifiutarla — si disabilita il comando invece di\r\n // riproporlo a ogni tentativo.\r\n this._isSaveAvailable.set(false);\r\n }\r\n // L'esito si **vede**: banner persistente con la categoria. Un salvataggio che\r\n // fallisce in silenzio e' peggio di nessun salvataggio.\r\n this._saveError.set(apiError);\r\n return of(undefined);\r\n }),\r\n takeUntilDestroyed(this.destroyRef),\r\n )\r\n .subscribe();\r\n }\r\n\r\n dismissError(): void {\r\n this._error.set(undefined);\r\n }\r\n\r\n dismissSaveError(): void {\r\n this._saveError.set(undefined);\r\n }\r\n\r\n private saveNoticeTimer: ReturnType<typeof setTimeout> | undefined;\r\n\r\n private showSaveNotice(): void {\r\n this.clearSaveNotice();\r\n this._justSaved.set(true);\r\n this.saveNoticeTimer = setTimeout(() => {\r\n this.saveNoticeTimer = undefined;\r\n this._justSaved.set(false);\r\n }, SAVE_NOTICE_MS);\r\n }\r\n\r\n private clearSaveNotice(): void {\r\n if (this.saveNoticeTimer !== undefined) {\r\n clearTimeout(this.saveNoticeTimer);\r\n this.saveNoticeTimer = undefined;\r\n }\r\n this._justSaved.set(false);\r\n }\r\n\r\n /**\r\n * Un host che non usa `DpeApiError` non deve far esplodere l'editor: cio' che non e'\r\n * categorizzato diventa `Transport`, che e' la categoria \"riprovabile\".\r\n */\r\n private toApiError(error: unknown): DpeApiError {\r\n if (DpeApiError.is(error)) {\r\n return error;\r\n }\r\n const message = error instanceof Error ? error.message : 'Errore inatteso dal backend.';\r\n return new DpeApiError('Transport', message, error);\r\n }\r\n}\r\n","/**\r\n * Il catalogo del linguaggio di formule, **conservato per nodo** — `frontend.md` §5-quater,\r\n * normativo.\r\n *\r\n * `getFormulaCatalog` non e' una chiamata leggera: lato libreria costa una passata completa di\r\n * grafo e di propagazione, e interroga il provider di metadati per **tutti** i datasource della\r\n * definizione, non solo per il cono a monte del nodo. Chiederlo a ogni apertura di un campo formula\r\n * e' il modo naturale di scriverlo e quello sbagliato, e non lascia traccia: non fallisce, si\r\n * limita a far pagare al backend N volte una risposta che non e' cambiata.\r\n *\r\n * Quindi: **una volta per nodo**, conservato, e invalidato quando cambia cio' che puo' cambiarne il\r\n * contenuto.\r\n *\r\n * Cosa questo store **non** fa, e non e' una dimenticanza:\r\n *\r\n * - **non valida niente.** Il catalogo dice cosa esiste, non se cio' che l'utente ha scritto va\r\n * bene. La grammatica e' del compilatore dell'ospite, il frontend non la conosce e non puo'\r\n * conoscerla: nessuna validazione di formula viene scritta in TypeScript, per nessun motivo\r\n * (ADR 0031/0036, `contracts.md` §7);\r\n * - **non decide il nome del linguaggio.** Quello e' `DpeEnvironment.expressionLanguage`, e arriva\r\n * anche quando il catalogo non arriva — che e' il caso piu' importante dei due, perche' un\r\n * dialetto fuori da `SupportedDialects` rende il catalogo nullo (ADR 0031 §5) proprio mentre\r\n * l'utente ha ancora bisogno di sapere in che lingua sta scrivendo;\r\n * - **non chiama se non deve.** Servono due cose: la primitiva **sovrascritta e non dichiarata non\r\n * servita** (`dpe-capabilities.ts`) e il compilatore che dichiara di descrivere il proprio\r\n * linguaggio (`providesFormulaCatalog`). Un compilatore laconico risponde un catalogo vuoto, che\r\n * e' legittimo (ADR 0036 §5): farsi pagare una passata di grafo per riceverlo e' spreco puro.\r\n *\r\n * Provider del **componente radice** come gli altri store: due editor sulla stessa pagina hanno due\r\n * documenti, quindi due cache.\r\n */\r\n\r\nimport { Injectable, Signal, computed, effect, inject, signal, untracked } from '@angular/core';\r\nimport { catchError, of, tap } from 'rxjs';\r\n\r\nimport { DpeBuilderApi } from '../api/dpe-builder-api';\r\nimport {\r\n DpeFormulaCatalog,\r\n DpeFormulaCatalogResult,\r\n DpeNodeSchemaInfo,\r\n} from '../model/dpe-api.model';\r\nimport { dpeReadCapabilities } from './dpe-capabilities';\r\nimport { DpeDocumentStore } from './dpe-document.store';\r\nimport { DpeFormulaCatalogOutcome, dpeFormulaCatalogOutcome } from './dpe-formula-catalog.view';\r\nimport { DpeValidationService } from './dpe-validation.service';\r\n\r\n/**\r\n * L'impronta di cio' che puo' cambiare una risposta di `getFormulaCatalog`: **il nome del\r\n * documento** e **gli schemi che il backend ha propagato**.\r\n *\r\n * Le due parti corrispondono a cio' che la risposta contiene, e nessuna e' un indovinello: i\r\n * simboli `Column` *sono* le colonne che quel nodo vede, e quali siano lo decide la propagazione\r\n * dello schema, che e' del backend (**I4**). Qualunque altra impronta — «e' cambiata la topologia»,\r\n * «e' cambiato un alias di sorgente» — richiederebbe di sapere *quali* modifiche cambiano uno\r\n * schema, cioe' di riscrivere I4 in TypeScript.\r\n *\r\n * Conseguenza voluta, ed e' la ragione per cui questa funzione e' pura e verificabile: scrivere\r\n * nel testo di una formula **non** cambia l'impronta, quindi non invalida nulla. Rinominare un\r\n * alias a monte la cambia, e il catalogo si rilegge alla prossima apertura del nodo.\r\n *\r\n * Il nome del documento entra perche' prima della prima validazione gli schemi sono vuoti: senza\r\n * di esso due documenti diversi appena aperti avrebbero la stessa impronta, e il secondo\r\n * erediterebbe il catalogo del primo.\r\n *\r\n * **Qui c'era una terza parte, la forma dei nodi, e non c'e' piu'.** Era entrata quando la risposta\r\n * portava `requiredForm`, che dipende dal tipo del nodo mentre lo schema in uscita no: cambiare una\r\n * trasformazione da `Expression` a `ComputeRelative` lasciava l'impronta identica e la pastiglia\r\n * diceva la forma vecchia. Ora la forma arriva con la validazione e non con questa risposta, e\r\n * l'ingresso non esiste piu': misurato sui due lati, il contratto — il compilatore riceve\r\n * `DpeExpressionCatalogRequest`, cioe' **dialetto, colonne a monte e parametri**, e non il tipo del\r\n * nodo — e il browser, dove lo stesso nodo prima e dopo il cambio di tipo riceve un catalogo\r\n * identico (le stesse 32 voci). Toglierla non e' una semplificazione: e' non pagare una richiesta\r\n * per una risposta che non puo' essere cambiata.\r\n *\r\n * **Due ingressi restano scoperti, e sono scoperti da sempre:** il `dialect` e i `parameters` della\r\n * definizione, che pure entrano nella richiesta al compilatore. Oggi nessuno dei due si puo'\r\n * modificare a documento aperto — l'editor non li scrive, e caricarne un altro azzera gli schemi,\r\n * quindi l'impronta si muove lo stesso. Il giorno in cui compare un editor dei parametri, il posto\r\n * da cui ripartire e' questo.\r\n */\r\nexport function dpeFormulaCatalogSignature(\r\n definitionName: string,\r\n schemas: readonly DpeNodeSchemaInfo[],\r\n): string {\r\n const parts = schemas.map(\r\n (schema) =>\r\n // Nessun `?? []`: `columns` e' obbligatoria nel contratto, e il ripiego avrebbe nascosto una\r\n // risposta fuori contratto sotto lo stesso valore di una collezione legittimamente vuota.\r\n `${schema.nodeName}:${schema.columns\r\n .map((column) => `${column.name}/${column.type.kind}`)\r\n .join(',')}`,\r\n );\r\n // `\\u0000` come separatore: non puo' comparire in un nome di nodo ne' di colonna, quindi due\r\n // impronte diverse non possono collidere per concatenazione.\r\n return `${definitionName}\\u0000${parts.join('|')}`;\r\n}\r\n\r\n/**\r\n * Una voce di cache: **l'involucro intero** e l'impronta con cui e' stato ottenuto.\r\n *\r\n * L'involucro e non il catalogo: un esito con diagnostiche e' una **risposta**, non un guasto, e\r\n * conservare il solo catalogo ne buttava via la meta' che spiega perche' non c'e'. Era il difetto\r\n * misurato in LF6 — dialetto non coperto e backend rotto indistinguibili — e si chiude qui, dove\r\n * la risposta entra.\r\n */\r\ninterface DpeCatalogEntry {\r\n readonly signature: string;\r\n readonly result: DpeFormulaCatalogResult;\r\n}\r\n\r\n@Injectable()\r\nexport class DpeFormulaCatalogStore {\r\n private readonly api = inject(DpeBuilderApi);\r\n private readonly document = inject(DpeDocumentStore);\r\n /**\r\n * La dichiarazione dell'ambiente si chiede **una volta all'apertura**, e chi la chiede e'\r\n * `DpeValidationService`. Rileggerla da qui sarebbe un secondo `getEnvironment` per la stessa\r\n * risposta — e per giunta due risposte che possono arrivare in ordine diverso.\r\n */\r\n private readonly validation = inject(DpeValidationService);\r\n\r\n private readonly capabilities = dpeReadCapabilities(this.api);\r\n\r\n private readonly _byNode = signal<ReadonlyMap<string, DpeCatalogEntry>>(new Map());\r\n private readonly _pending = signal<ReadonlySet<string>>(new Set());\r\n /**\r\n * Il nodo di cui si vuole il catalogo — **uno**, quello aperto.\r\n *\r\n * Esiste per un difetto trovato nel browser e non da nessun test: la disponibilita' dipende da\r\n * `getEnvironment`, che e' una chiamata di rete, e chi apre un nodo subito dopo aver aperto\r\n * l'editor chiede il catalogo **prima** che l'ambiente abbia risposto. La richiesta cadeva nel\r\n * vuoto e non veniva piu' ripetuta, perche' l'apertura del nodo era l'unico innesco: il campo\r\n * restava senza catalogo per sempre, senza errori, e riaprire lo stesso nodo non cambiava\r\n * niente. Tenere qui **l'intenzione** invece di agire subito la rende ripetibile quando la\r\n * condizione che mancava si verifica.\r\n */\r\n private readonly _wanted = signal<string | undefined>(undefined);\r\n\r\n /**\r\n * Il catalogo si puo' chiedere: primitiva disponibile **e** compilatore che dichiara di\r\n * descrivere il proprio linguaggio. Le due meta' restano separate perche' sono due fatti\r\n * diversi — il trasporto e il compilatore — e un solo booleano non permetterebbe di capire\r\n * quale dei due manca.\r\n */\r\n readonly isAvailable: Signal<boolean> = computed(\r\n () => this.capabilities.getFormulaCatalog && this.validation.providesFormulaCatalog(),\r\n );\r\n\r\n /**\r\n * L'impronta corrente: cambia quando cambia cio' che puo' cambiare una risposta, cioe' gli\r\n * **schemi** che il backend ha propagato — sono loro a decidere i simboli.\r\n *\r\n * Legge il documento, e va bene: il valore prodotto e' una stringa, e a parita' di stringa un\r\n * `computed` non notifica nessuno. Scrivere in una formula ricalcola l'impronta e non muove\r\n * niente — cioe' nessuna richiesta a ogni battitura, che resta la regola di `frontend.md`\r\n * §5-quater.\r\n */\r\n readonly signature = computed(() =>\r\n dpeFormulaCatalogSignature(this.document.definition().name ?? '', this.document.schemas()),\r\n );\r\n\r\n /**\r\n * La prima validazione e' tornata, cioe' **gli schemi si conoscono**.\r\n *\r\n * Prima di allora il catalogo non si chiede, e non e' prudenza: i simboli `Column` sono le colonne\r\n * che il nodo vede, e chiederli quando gli schemi non sono ancora arrivati significa ricevere una\r\n * risposta che l'istante dopo e' scaduta — cioe' **due** chiamate per la stessa apertura, dove\r\n * ogni chiamata costa al backend una passata completa di grafo e di metadati. Misurato: aprire un\r\n * nodo subito dopo l'editor faceva salire il contatore di due.\r\n */\r\n private readonly hasValidated = computed(() => this.document.validateResult() !== undefined);\r\n\r\n /**\r\n * L'**involucro** noto per un nodo, se e' stato ottenuto con l'impronta corrente.\r\n *\r\n * Legge i signal, quindi si usa dentro un `computed` e resta reattivo — lo stesso schema di\r\n * `DpeMetadataStore.stateOf`.\r\n *\r\n * **Scade con l'impronta**: un catalogo ottenuto quando gli schemi erano altri non si restituisce\r\n * come se fosse attuale — dichiarerebbe disponibili simboli che non esistono piu'. Finche' il nodo\r\n * resta quello aperto, l'effect del costruttore ne chiede subito uno nuovo, quindi la scadenza si\r\n * vede al piu' come una sparizione momentanea del conteggio, non come un campo che resta muto.\r\n *\r\n * Scade anche l'esito d'errore, ed e' giusto: un `DPE_CATALOG_UNAVAILABLE` nasce da uno schema\r\n * che non si risolve, e cambiare gli schemi e' esattamente cio' che puo' averlo risolto.\r\n */\r\n resultOf(nodeName: string): DpeFormulaCatalogResult | undefined {\r\n const entry = this._byNode().get(nodeName);\r\n return entry && entry.signature === this.signature() ? entry.result : undefined;\r\n }\r\n\r\n /**\r\n * Il catalogo consegnato, **quando e' stato prodotto**. Puo' legittimamente essere vuoto: chi\r\n * rende non deve confondere «vuoto» con «assente», e per questo la decisione di cosa mostrare\r\n * passa da `outcomeOf` e non da qui.\r\n */\r\n catalogOf(nodeName: string): DpeFormulaCatalog | undefined {\r\n return this.resultOf(nodeName)?.catalog ?? undefined;\r\n }\r\n\r\n /**\r\n * Quale dei tre esiti del contratto si sta guardando — piu' «non lo so ancora».\r\n *\r\n * E' cio' che i componenti consumano: la tricotomia si legge **una volta**, in una funzione\r\n * pura, e non si riscrive in ogni ispettore con un `if` leggermente diverso.\r\n */\r\n outcomeOf(nodeName: string): DpeFormulaCatalogOutcome {\r\n return dpeFormulaCatalogOutcome(this.resultOf(nodeName));\r\n }\r\n\r\n /*\r\n * `requiredFormOf` **non sta piu' qui**, e non e' un trasloco di comodo: la forma arriva con lo\r\n * schema del nodo (`DpeNodeSchemaInfo.requiredForm`), quindi chiederla a questo store\r\n * significherebbe far dipendere dal catalogo un dato che col catalogo non c'entra — e farle\r\n * ereditare le due proprieta' sbagliate di una voce di cache, cioe' esserci solo se la rotta e'\r\n * stata chiamata e scadere quando scade il catalogo. Si legge da `DpeDocumentStore.schemaByNode`,\r\n * dove stanno gia' colonne e compilabilita' dello stesso record.\r\n */\r\n\r\n /** Una richiesta per questo nodo e' in volo: lo stato di attesa si mostra sempre. */\r\n isLoading(nodeName: string): boolean {\r\n return this._pending().has(nodeName);\r\n }\r\n\r\n constructor() {\r\n /*\r\n * L'innesco della richiesta, e dipende da **tre** signal: il nodo che si vuole, la\r\n * disponibilita', l'impronta. Nient'altro — in particolare **non** il documento: leggere qui\r\n * `document.definition()` significherebbe una richiesta di catalogo a ogni battitura, cioe' una\r\n * passata di grafo e di metadati al backend per ogni carattere digitato (`frontend.md`\r\n * §5-quater). L'impronta cambia solo quando cambiano gli schemi che il backend ha propagato,\r\n * quindi scrivere dentro una formula non la muove.\r\n *\r\n * Le altre due dipendenze chiudono due difetti trovati **nel browser**, non da un test:\r\n *\r\n * - la **disponibilita'** arriva da `getEnvironment`, cioe' dalla rete: chi apre un nodo subito\r\n * dopo l'editor chiedeva il catalogo prima che si sapesse se chiederlo, e la richiesta cadeva\r\n * nel vuoto per sempre;\r\n * - l'**impronta** cambia quando la prima validazione risponde, che e' un istante dopo\r\n * l'apertura del nodo. Il catalogo chiesto prima risultava registrato con l'impronta\r\n * precedente, cioe' **scaduto appena nato**: la chiamata partiva, la risposta arrivava, e il\r\n * campo non mostrava niente. Il contatore diceva 1 e lo schermo diceva zero. Da qui anche la\r\n * guardia su `hasValidated`: chiedere prima di sapere gli schemi costa **due** chiamate per\r\n * una sola apertura, e il rimedio giusto e' aspettare, non rileggere.\r\n */\r\n effect(() => {\r\n const nodeName = this._wanted();\r\n const signature = this.signature();\r\n if (!nodeName || !this.isAvailable() || !this.hasValidated()) {\r\n return;\r\n }\r\n untracked(() => this.fetchIfNeeded(nodeName, signature));\r\n });\r\n }\r\n\r\n /**\r\n * Chiede il catalogo per un nodo, **se serve**. Idempotente: chiamarla a ogni apertura della\r\n * finestra non produce chiamate, chiamarla in un ciclo nemmeno.\r\n *\r\n * Fa **due** cose, e servono entrambe:\r\n *\r\n * 1. prova subito, sotto `untracked`, cosi' l'esito e' immediato quando le condizioni ci sono\r\n * gia'. Affidarsi al solo `_wanted` non basterebbe: riaperto lo **stesso** nodo il signal non\r\n * cambia valore e l'effect non riparte;\r\n * 2. registra l'intenzione in `_wanted`, cosi' quando cambia una delle condizioni che mancavano\r\n * — la disponibilita' dell'ambiente, l'impronta degli schemi — la richiesta viene onorata\r\n * senza che l'utente debba riaprire il nodo.\r\n *\r\n * **Ogni lettura di signal e' in `untracked`**, e non e' prudenza: i chiamanti sono `effect` degli\r\n * ispettori, e un `this.document.definition()` letto qui diventerebbe una dipendenza di\r\n * quell'effect — cioe' una richiesta di catalogo **a ogni battitura**, che e' il difetto che\r\n * questo store esiste per evitare. Il sintomo non sarebbe un errore: sarebbe un backend che\r\n * macina grafi mentre qualcuno scrive.\r\n */\r\n requestFor(nodeName: string): void {\r\n if (!nodeName) {\r\n return;\r\n }\r\n this._wanted.set(nodeName);\r\n untracked(() => {\r\n if (this.isAvailable() && this.hasValidated()) {\r\n this.fetchIfNeeded(nodeName, this.signature());\r\n }\r\n });\r\n }\r\n\r\n /**\r\n * La chiamata, **se serve davvero**: non c'e' gia' una richiesta in volo per quel nodo, e cio'\r\n * che e' in cache non e' stato ottenuto con l'impronta corrente.\r\n *\r\n * Tutte le letture stanno sotto `untracked` da parte dei chiamanti: sono signal, e legarli\r\n * all'effect vorrebbe dire rileggere il catalogo a ogni modifica del documento.\r\n */\r\n private fetchIfNeeded(nodeName: string, signature: string): void {\r\n if (this._pending().has(nodeName)) {\r\n return;\r\n }\r\n const known = this._byNode().get(nodeName);\r\n if (known && known.signature === signature) {\r\n return;\r\n }\r\n this.fetch(nodeName, signature, this.document.definition());\r\n }\r\n\r\n /**\r\n * Rilettura forzata, per chi vuole il catalogo aggiornato senza aspettare un cambio di impronta.\r\n * Non ha un consumatore nell'editor di oggi ed esiste perche' una cache senza modo di essere\r\n * invalidata a mano e' una cache che prima o poi si aggira con un ricaricamento della pagina.\r\n */\r\n refresh(nodeName: string): void {\r\n untracked(() => {\r\n if (!this.isAvailable() || this._pending().has(nodeName)) {\r\n return;\r\n }\r\n this.fetch(nodeName, this.signature(), this.document.definition());\r\n });\r\n }\r\n\r\n\r\n private fetch(\r\n nodeName: string,\r\n signature: string,\r\n definition: Parameters<DpeBuilderApi['getFormulaCatalog']>[0],\r\n ): void {\r\n this.setPending(nodeName, true);\r\n this.api\r\n .getFormulaCatalog(definition, nodeName)\r\n .pipe(\r\n tap((result) => {\r\n this.setPending(nodeName, false);\r\n /*\r\n * Si registra con l'impronta **della richiesta**, non con quella corrente: se il\r\n * documento e' cambiato mentre la risposta era in volo, questa voce risulta subito\r\n * scaduta e verra' riletta alla prossima apertura. L'alternativa — registrarla come\r\n * fresca — spaccerebbe per attuale un catalogo calcolato su un documento precedente.\r\n */\r\n this.put(nodeName, { signature, result });\r\n /*\r\n * E se nel frattempo l'impronta si e' mossa, si rilegge **subito**.\r\n *\r\n * Senza questa riga il caso normale non funziona, ed e' il difetto piu' caro del lotto:\r\n * la prima validazione risponde un istante dopo l'apertura della finestra, quindi mentre\r\n * la richiesta e' in volo l'impronta cambia. L'effect riparte, trova la richiesta\r\n * `pending` e rinuncia — correttamente, non si duplicano le chiamate — e la risposta che\r\n * arriva subito dopo viene registrata con l'impronta **precedente**, cioe' scaduta appena\r\n * nata. Esito a schermo: il contatore diceva 1 e il campo non mostrava niente.\r\n */\r\n untracked(() => this.fetchIfNeeded(nodeName, this.signature()));\r\n }),\r\n catchError(() => {\r\n this.setPending(nodeName, false);\r\n /*\r\n * Un guasto **di trasporto** non si registra: niente in cache significa «non lo so», e\r\n * la prossima apertura riprova. Non si mostra come errore dell'editor — senza catalogo\r\n * il campo formula e' un campo di testo, che e' cio' che era prima dell'ADR 0036, e\r\n * l'etichetta del linguaggio arriva comunque dall'ambiente.\r\n *\r\n * Da non confondere con un esito **con diagnostiche**, che passa dal ramo sopra: quello\r\n * e' una risposta, si conserva e si mostra. Sono le due cose che prima arrivavano qui\r\n * insieme, e distinguerle e' il motivo per cui la rotta consegna l'involucro.\r\n */\r\n return of(undefined);\r\n }),\r\n )\r\n .subscribe();\r\n }\r\n\r\n private put(nodeName: string, entry: DpeCatalogEntry): void {\r\n const next = new Map(this._byNode());\r\n next.set(nodeName, entry);\r\n this._byNode.set(next);\r\n }\r\n\r\n private setPending(nodeName: string, pending: boolean): void {\r\n const next = new Set(this._pending());\r\n if (pending) {\r\n next.add(nodeName);\r\n } else {\r\n next.delete(nodeName);\r\n }\r\n this._pending.set(next);\r\n }\r\n}\r\n","/**\r\n * Layout automatico del DAG con dagre.\r\n *\r\n * Due cose non ovvie, ed entrambe sono il motivo per cui esiste questo involucro invece di\r\n * chiamare dagre dai componenti:\r\n *\r\n * 1. **dagre ragiona in centri**, il canvas vuole l'angolo in alto a sinistra. La conversione\r\n * sta qui, una volta: fatta a valle, in ogni chiamante, sbaglia di mezzo nodo e il\r\n * disallineamento si nota solo a occhio;\r\n * 2. **dagre non e' ESM**: sta in `allowedCommonJsDependencies` di `angular.json`, altrimenti\r\n * la build avvisa a ogni compilazione.\r\n *\r\n * Input minimale di proposito: nomi e misure, piu' gli archi. Il servizio non conosce il\r\n * documento e non lo scrive — restituisce le posizioni, e chi lo invoca decide cosa farne.\r\n */\r\n\r\nimport { Injectable } from '@angular/core';\r\nimport dagre from 'dagre';\r\n\r\nexport interface DpeLayoutNodeInput {\r\n name: string;\r\n width: number;\r\n height: number;\r\n}\r\n\r\nexport interface DpeLayoutEdgeInput {\r\n /** Nome del nodo a monte. */\r\n from: string;\r\n /** Nome del nodo a valle. */\r\n to: string;\r\n /** Identificatore dell'arco: dagre ne ha bisogno per ammettere archi paralleli. */\r\n id: string;\r\n}\r\n\r\nexport interface DpeLayoutPosition {\r\n x: number;\r\n y: number;\r\n}\r\n\r\nexport type DpeLayoutDirection = 'TB' | 'LR';\r\n\r\nexport interface DpeLayoutOptions {\r\n /** Default `TB`: i dati scendono dalle sorgenti ai sink. */\r\n direction?: DpeLayoutDirection;\r\n /** Distanza fra i livelli. */\r\n rankSeparation?: number;\r\n /** Distanza fra i nodi dello stesso livello. */\r\n nodeSeparation?: number;\r\n /** Angolo in alto a sinistra del grafo risultante. */\r\n origin?: DpeLayoutPosition;\r\n}\r\n\r\n@Injectable()\r\nexport class DpeLayoutService {\r\n /**\r\n * Calcola le posizioni. La chiave della mappa e' il **nome** del nodo, non il suo id sul\r\n * canvas: il nome e' cio' che identifica un nodo nel documento, e passare dall'id\r\n * costringerebbe questo servizio a conoscere la costruzione degli id.\r\n */\r\n layout(\r\n nodes: readonly DpeLayoutNodeInput[],\r\n edges: readonly DpeLayoutEdgeInput[],\r\n options?: DpeLayoutOptions,\r\n ): Map<string, DpeLayoutPosition> {\r\n const origin = options?.origin ?? { x: 60, y: 60 };\r\n\r\n const graph = new dagre.graphlib.Graph({ multigraph: true, compound: false });\r\n graph.setGraph({\r\n rankdir: options?.direction ?? 'TB',\r\n ranksep: options?.rankSeparation ?? 90,\r\n nodesep: options?.nodeSeparation ?? 60,\r\n marginx: 0,\r\n marginy: 0,\r\n });\r\n graph.setDefaultEdgeLabel(() => ({}));\r\n\r\n const known = new Set<string>();\r\n for (const node of nodes) {\r\n graph.setNode(node.name, { width: node.width, height: node.height });\r\n known.add(node.name);\r\n }\r\n\r\n for (const edge of edges) {\r\n // Un arco con un capo sconosciuto e' un riferimento rotto: si disegna comunque sul\r\n // canvas, ma non vincola il layout — dagre creerebbe un nodo fantasma con misure zero.\r\n if (!known.has(edge.from) || !known.has(edge.to)) {\r\n continue;\r\n }\r\n graph.setEdge(edge.from, edge.to, {}, edge.id);\r\n }\r\n\r\n dagre.layout(graph);\r\n\r\n const positions = new Map<string, DpeLayoutPosition>();\r\n let minX = Number.POSITIVE_INFINITY;\r\n let minY = Number.POSITIVE_INFINITY;\r\n\r\n for (const node of nodes) {\r\n const laid = graph.node(node.name) as { x?: number; y?: number } | undefined;\r\n if (!laid || laid.x === undefined || laid.y === undefined) {\r\n continue;\r\n }\r\n const x = laid.x - node.width / 2;\r\n const y = laid.y - node.height / 2;\r\n positions.set(node.name, { x, y });\r\n minX = Math.min(minX, x);\r\n minY = Math.min(minY, y);\r\n }\r\n\r\n // Trasla il grafo sull'origine scelta: dagre puo' produrre coordinate negative, e un nodo\r\n // a coordinate negative finisce fuori dall'area visibile appena il canvas si apre.\r\n if (Number.isFinite(minX) && Number.isFinite(minY)) {\r\n const deltaX = origin.x - minX;\r\n const deltaY = origin.y - minY;\r\n for (const [name, position] of positions) {\r\n positions.set(name, {\r\n x: Math.round(position.x + deltaX),\r\n y: Math.round(position.y + deltaY),\r\n });\r\n }\r\n }\r\n\r\n return positions;\r\n }\r\n}\r\n","/**\r\n * Cache locale del provider di metadati dell'host.\r\n *\r\n * Tabelle e campi arrivano da `DpeBuilderApi`, cioe' dal **provider dell'host** e non dai\r\n * cataloghi del database (ADR 0004). Due cose che questa cache non fa, di proposito:\r\n *\r\n * - **non appiattisce i tre esiti**. `Declared`, `DeclaredWithoutFields` (\"la tabella c'e', i\r\n * campi non li so\") e `Unknown` restano distinti fino alla UI: chi vede una tendina vuota\r\n * deve poter sapere quale dei tre casi sta guardando, perche' nel secondo il nome del campo\r\n * si digita a mano e non c'e' niente da correggere;\r\n * - **non giudica**. Se una tabella non e' nel catalogo, il rilievo autorevole\r\n * (`DPE_REF_UNKNOWN_TABLE`) arriva dalla validazione; qui si sa solo che non si sa.\r\n *\r\n * L'elenco delle tabelle puo' essere grande: si chiede una volta, si tiene, e chi lo mostra\r\n * filtra invece di riversarlo in un `<select>`.\r\n */\r\n\r\nimport { Injectable, inject, signal } from '@angular/core';\r\nimport { catchError, of, tap } from 'rxjs';\r\n\r\nimport { DpeBuilderApi } from '../api/dpe-builder-api';\r\nimport { TableDescriptor, TableLookupOutcome, TableSummary } from '../model/dpe-api.model';\r\n\r\nexport interface DpeTableState {\r\n outcome: TableLookupOutcome;\r\n table?: TableDescriptor;\r\n}\r\n\r\n@Injectable()\r\nexport class DpeMetadataStore {\r\n private readonly api = inject(DpeBuilderApi);\r\n\r\n private readonly _tables = signal<readonly TableSummary[]>([]);\r\n private readonly _isLoadingTables = signal(false);\r\n private readonly _tablesRequested = signal(false);\r\n private readonly _byName = signal<ReadonlyMap<string, DpeTableState>>(new Map());\r\n private readonly pending = new Set<string>();\r\n\r\n readonly tables = this._tables.asReadonly();\r\n readonly isLoadingTables = this._isLoadingTables.asReadonly();\r\n\r\n /** Idempotente: la prima chiamata carica, le successive non fanno nulla. */\r\n loadTables(): void {\r\n if (this._tablesRequested()) {\r\n return;\r\n }\r\n this._tablesRequested.set(true);\r\n this._isLoadingTables.set(true);\r\n this.api\r\n .getTables()\r\n .pipe(\r\n tap((tables) => {\r\n this._tables.set(tables);\r\n this._isLoadingTables.set(false);\r\n }),\r\n catchError(() => {\r\n // Elenco vuoto significa \"non lo so\", non \"nessuna tabella\": la UI lascia digitare\r\n // il nome invece di impedire di scriverlo.\r\n this._isLoadingTables.set(false);\r\n return of([]);\r\n }),\r\n )\r\n .subscribe();\r\n }\r\n\r\n /** Stato noto di una tabella, se gia' richiesta. */\r\n stateOf(tableName: string): DpeTableState | undefined {\r\n return this._byName().get(tableName);\r\n }\r\n\r\n /** Chiede la descrizione di una tabella una sola volta per nome. */\r\n describe(tableName: string): void {\r\n if (!tableName || this.pending.has(tableName) || this._byName().has(tableName)) {\r\n return;\r\n }\r\n this.pending.add(tableName);\r\n this.api\r\n .getTable(tableName)\r\n .pipe(\r\n tap((result) => {\r\n this.pending.delete(tableName);\r\n this.put(tableName, { outcome: result.outcome, table: result.table });\r\n }),\r\n catchError(() => {\r\n this.pending.delete(tableName);\r\n // Un guasto di trasporto non e' `Unknown`: `Unknown` e' una risposta del provider.\r\n // Non si registra nulla, cosi' un tentativo successivo riprova.\r\n return of(undefined);\r\n }),\r\n )\r\n .subscribe();\r\n }\r\n\r\n private put(tableName: string, state: DpeTableState): void {\r\n const next = new Map(this._byName());\r\n next.set(tableName, state);\r\n this._byName.set(next);\r\n }\r\n}\r\n","/**\r\n * I tracciati SVG delle icone di categoria, uno per collezione di nodi.\r\n *\r\n * **Perche' tracciati e non glifi Unicode** (divergenza motivata dal progetto di riferimento,\r\n * `frontend.md` §3-ter): i glifi non hanno metriche ne' linea di base coerenti fra loro e su\r\n * alcune piattaforme diventano emoji a colori — cioe' proprio la resa sbagliata quando stanno\r\n * in colonna in una palette. Un tracciato disegnato su una griglia 24x24 con `currentColor` si\r\n * colora con una sola regola CSS e vale identico in tema chiaro e scuro.\r\n *\r\n * **Nessun asset e nessuna dipendenza**: sono stringhe, e il componente che le rende le lega con\r\n * `[attr.d]` senza mai toccare `innerHTML` — quindi niente `bypassSecurityTrust*`.\r\n *\r\n * Il disegno e' pensato per la resa a **16px**: pochi tratti, nessun dettaglio sotto i 2 px di\r\n * griglia, e sagome che restano distinguibili l'una dall'altra quando sono incolonnate.\r\n */\r\n\r\nimport { DpeNodeCollectionName } from '../model/dpe-definition.model';\r\nimport { dpeNodeCategory } from './dpe-node-catalog';\r\n\r\n/**\r\n * La famiglia cromatica di una categoria. **Non e' una tassonomia nuova**: si deriva per intero\r\n * da `isSource`/`isSink`/`isNotCompilableInV1` del catalogo, che restano l'unica descrizione dei\r\n * tipi di nodo. Serve solo a scegliere un colore.\r\n */\r\nexport type DpeNodeIconFamily = 'source' | 'transform' | 'sink' | 'uncompilable';\r\n\r\nexport function dpeNodeIconFamily(collection: DpeNodeCollectionName): DpeNodeIconFamily {\r\n const category = dpeNodeCategory(collection);\r\n if (category.isSource) {\r\n return 'source';\r\n }\r\n if (category.isSink) {\r\n return 'sink';\r\n }\r\n // Dopo sorgenti e scritture: un sink non compilabile non esiste, e se un giorno esistesse\r\n // resterebbe piu' utile vederlo come sink.\r\n if (category.isNotCompilableInV1) {\r\n return 'uncompilable';\r\n }\r\n return 'transform';\r\n}\r\n\r\n/** Classe CSS della famiglia: un solo posto la costruisce, cosi' i fogli restano allineati. */\r\nexport function dpeNodeIconFamilyClass(collection: DpeNodeCollectionName): string {\r\n return `dpe-family--${dpeNodeIconFamily(collection)}`;\r\n}\r\n\r\n/**\r\n * I tracciati, per collezione. Ogni voce e' l'elenco degli attributi `d` di un `<path>`; il\r\n * contorno e' `stroke`, mai `fill`, cosi' l'icona pesa uguale su qualunque sfondo.\r\n */\r\nexport const DPE_NODE_ICON_PATHS: Readonly<Record<DpeNodeCollectionName, readonly string[]>> = {\r\n // Cilindro di database: ellisse superiore, fianchi, e la riga di mezzo che lo fa leggere\r\n // come un solido invece che come un ovale con due gambe.\r\n dataSources: [\r\n 'M4 6a8 3 0 1 0 16 0a8 3 0 1 0 -16 0',\r\n 'M4 6v12c0 1.66 3.58 3 8 3s8-1.34 8-3V6',\r\n 'M4 12c0 1.66 3.58 3 8 3s8-1.34 8-3',\r\n ],\r\n // Imbuto.\r\n filters: ['M3 4h18l-7 8.5V20l-4-2v-5.5z'],\r\n // Due cerchi che si intersecano: ogni cerchio e' due archi da 180 gradi. I centri stanno a 7\r\n // unita' e il raggio e' 4.5: con una sovrapposizione maggiore, a 16px, si leggevano come un\r\n // cerchio con un anello dentro invece che come due insiemi che si incrociano.\r\n joins: ['M8.5 7.5a4.5 4.5 0 1 0 0 9 4.5 4.5 0 1 0 0-9', 'M15.5 7.5a4.5 4.5 0 1 0 0 9 4.5 4.5 0 1 0 0-9'],\r\n // Tre barre di altezza diversa. Rettangoli e non segmenti: a 16px un segmento verticale\r\n // spesso 2 non si distingue da una barra sottile, e l'insieme si confonde con l'unione.\r\n aggregates: ['M4 11h4v9H4z', 'M10 5h4v15h-4z', 'M16 14h4v6h-4z'],\r\n // Due frecce affiancate in versi opposti.\r\n transforms: ['M3 8h13', 'M12 4l4 4-4 4', 'M21 16H8', 'M12 12l-4 4 4 4'],\r\n // Due lastre impilate.\r\n appends: ['M4 5h16v5H4z', 'M4 14h16v5H4z'],\r\n // Albero: un nodo in alto, due sotto, collegati.\r\n hierarchyPaths: ['M9 3h6v4H9z', 'M12 7v3', 'M6 10h12', 'M6 10v3', 'M18 10v3', 'M3 13h6v4H3z', 'M15 13h6v4h-6z'],\r\n // Spezzata crescente, con la punta che dichiara la direzione.\r\n forecasts: ['M21 7l-8 8-4-4-6 6', 'M15 7h6v6'],\r\n // Tassello di puzzle: una bozza sopra, un incavo a destra. Due sole sporgenze e non quattro —\r\n // a 16px il tassello \"completo\" diventava un quadrato con quattro tacche indistinguibili.\r\n customNodes: ['M4 4h5.5a2.5 2.5 0 0 1 5 0H20v5.5a2.5 2.5 0 0 0 0 5V20H4z'],\r\n // Freccia che entra in un contenitore.\r\n writebacks: ['M12 3v10', 'M8 9l4 4 4-4', 'M4 14v4a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-4'],\r\n // Lo stesso contenitore, con un lucchetto al posto della freccia.\r\n atomicWritebacks: [\r\n 'M10 7V5.5a2 2 0 0 1 4 0V7',\r\n 'M8.5 7h7v5h-7z',\r\n 'M4 15v3a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-3',\r\n ],\r\n};\r\n\r\n/**\r\n * I tracciati di una collezione. Il ripiego vuoto non e' pigrizia: una collezione aggiunta al\r\n * modello e non ancora disegnata deve produrre una pastiglia vuota, non un errore in console\r\n * ne' un `undefined` dentro `[attr.d]`.\r\n */\r\nexport function dpeNodeIconPaths(collection: DpeNodeCollectionName): readonly string[] {\r\n return DPE_NODE_ICON_PATHS[collection] ?? [];\r\n}\r\n","/**\r\n * Geometria della viewport: funzioni pure, nessun DOM.\r\n *\r\n * Serve a una sola domanda, che si pone quando si naviga da una diagnostica al nodo: **il nodo\r\n * e' interamente in vista?** Se lo e', non si sposta niente — una panoramica non richiesta\r\n * disorienta quanto la sua assenza. Se non lo e', si centra la **vista**, non il nodo: le\r\n * coordinate del documento non si toccano.\r\n *\r\n * Sta qui e non nel componente perche' e' esattamente il tipo di logica che si sbaglia in\r\n * silenzio (un `>` al posto di un `>=`, un margine dimenticato) e che si verifica senza browser.\r\n */\r\n\r\n/** Rettangolo in coordinate di schermo, la forma che `getBoundingClientRect` restituisce. */\r\nexport interface DpeRect {\r\n left: number;\r\n top: number;\r\n right: number;\r\n bottom: number;\r\n}\r\n\r\n/**\r\n * Vero quando `target` sta **interamente** dentro `view`, lasciando `margin` pixel di respiro su\r\n * ogni lato. Il margine non e' un vezzo: un nodo tangente al bordo e' tecnicamente visibile e\r\n * praticamente illeggibile, e senza respiro il criterio \"e' visibile\" scatterebbe anche quando\r\n * dell'ombra e del connettore di uscita non si vede nulla.\r\n */\r\nexport function dpeIsRectFullyVisible(target: DpeRect, view: DpeRect, margin = 0): boolean {\r\n return (\r\n target.left >= view.left + margin &&\r\n target.top >= view.top + margin &&\r\n target.right <= view.right - margin &&\r\n target.bottom <= view.bottom - margin\r\n );\r\n}\r\n","/**\r\n * L'icona di una categoria di nodo.\r\n *\r\n * Rende markup SVG **statico** e lega soltanto `[attr.d]` dei tracciati: nessun `innerHTML`,\r\n * quindi nessun bypass del sanitizer e nessuna dipendenza per le icone. `d` non e' un attributo\r\n * sensibile — e' l'unico punto in cui il dato entra nel markup, ed e' un dato nostro, non del\r\n * documento.\r\n *\r\n * `aria-hidden`: l'icona **non e' informazione**, l'etichetta accanto lo e' gia'. Annunciarla\r\n * farebbe leggere due volte la stessa cosa a chi usa uno screen reader.\r\n *\r\n * La stessa icona compare in tre posti — palette, card del canvas, intestazione dell'ispettore —\r\n * perche' cio' che si trascina deve assomigliare a cio' che compare.\r\n */\r\n\r\nimport { ChangeDetectionStrategy, Component, computed, input } from '@angular/core';\r\n\r\nimport { dpeNodeIconPaths } from '../../core/dpe-node-icons';\r\nimport { DpeNodeCollectionName } from '../../model/dpe-definition.model';\r\n\r\n@Component({\r\n selector: 'dpe-node-icon',\r\n standalone: true,\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n template: `\r\n <svg\r\n viewBox=\"0 0 24 24\"\r\n width=\"16\"\r\n height=\"16\"\r\n fill=\"none\"\r\n stroke=\"currentColor\"\r\n stroke-width=\"2\"\r\n stroke-linecap=\"round\"\r\n stroke-linejoin=\"round\"\r\n aria-hidden=\"true\"\r\n >\r\n <!-- track $index: i tracciati sono una lista ricalcolata, e due uguali sono legittimi. -->\r\n @for (path of paths(); track $index) {\r\n <path [attr.d]=\"path\" />\r\n }\r\n </svg>\r\n `,\r\n})\r\nexport class DpeNodeIconComponent {\r\n readonly collection = input.required<DpeNodeCollectionName>();\r\n\r\n protected readonly paths = computed(() => dpeNodeIconPaths(this.collection()));\r\n}\r\n","/**\r\n * View model del canvas.\r\n *\r\n * Gli **id** non si costruiscono qui: vengono da `core/dpe-graph.ts`, che e' l'unico posto che\r\n * li conosce. Qui stanno solo le misure, e stanno qui perche' servono a due consumatori che\r\n * devono concordare: il CSS che disegna la card e dagre che calcola il layout. Se divergono,\r\n * il layout automatico lascia i nodi sovrapposti.\r\n */\r\n\r\nimport { DiagnosticSeverity } from '../../model/dpe-diagnostics.model';\r\nimport { DpeNodeCollectionName } from '../../model/dpe-definition.model';\r\n\r\n/** Altezza nominale della card: non cambia con il numero di porte, che stanno in fila sopra. */\r\nexport const DPE_NODE_HEIGHT = 104;\r\n\r\n/**\r\n * Larghezza per numero di porte di ingresso. Le porte stanno in fila sul bordo **superiore**,\r\n * quindi e' la larghezza a dipendere dal loro numero.\r\n *\r\n * E' una tabella a scaglioni e non una formula perche' la larghezza si applica con una\r\n * **classe**: @foblex/flow riscrive l'attributo `style` del nodo a ogni riposizionamento e\r\n * qualunque `[style.width]` di Angular verrebbe cancellato al primo spostamento. I valori qui\r\n * e quelli di `.dpe-node--ports-N` nel CSS del componente devono restare uguali.\r\n */\r\nconst PORT_WIDTHS = [230, 230, 260, 320, 380];\r\n\r\nconst MAX_PORT_BUCKET = PORT_WIDTHS.length - 1;\r\n\r\nfunction portBucket(portCount: number): number {\r\n return Math.min(Math.max(portCount, 0), MAX_PORT_BUCKET);\r\n}\r\n\r\nexport function dpeNodeWidth(portCount: number): number {\r\n return PORT_WIDTHS[portBucket(portCount)];\r\n}\r\n\r\nexport function dpeNodeWidthClass(portCount: number): string {\r\n return `dpe-node--ports-${portBucket(portCount)}`;\r\n}\r\n\r\nexport interface DpeCanvasPort {\r\n id: string;\r\n slot: string;\r\n index?: number;\r\n /** Etichetta della porta: si mostra solo dove le porte sono piu' di una. */\r\n label: string;\r\n /** Nome collegato, assente se la porta e' libera. */\r\n sourceName?: string;\r\n /** Il nome collegato non corrisponde a nessun nodo: si dice sulla porta. */\r\n isBroken: boolean;\r\n}\r\n\r\nexport interface DpeCanvasNode {\r\n /** Nome nel documento: l'identita', anche per la selezione. */\r\n name: string;\r\n /** Id per il canvas, costruito da `dpeNodeId`. */\r\n id: string;\r\n collection: DpeNodeCollectionName;\r\n categoryLabel: string;\r\n label: string;\r\n subtitle: string | null;\r\n position: { x: number; y: number };\r\n ports: DpeCanvasPort[];\r\n showsPortLabels: boolean;\r\n widthClass: string;\r\n /**\r\n * Classe della famiglia cromatica (`dpe-family--*`): la calcola `core/dpe-node-icons.ts` dai\r\n * flag del catalogo. Sta nel view model e non nel template perche' e' la stessa che usa la\r\n * palette, e due modi di derivarla sarebbero due colori diversi per lo stesso tipo di nodo.\r\n */\r\n familyClass: string;\r\n outputPortId: string;\r\n /** I sink non producono uno schema: la porta di uscita non si disegna... */\r\n hasOutput: boolean;\r\n /** Gravita' peggiore fra le diagnostiche del nodo. */\r\n severity: DiagnosticSeverity | null;\r\n diagnosticCount: number;\r\n /**\r\n * Nodo **valido ma non compilabile** (**I10**): stato distinto da \"non valido\", con un\r\n * colore proprio. Sono due situazioni diverse e l'utente deve capire subito quale guarda.\r\n */\r\n isNotCompilable: boolean;\r\n /**\r\n * Numero di colonne dello schema di uscita. `null` significa **nessuna pastiglia**, e ci sono\r\n * due modi di arrivarci:\r\n *\r\n * - il backend non ha dichiarato di aver derivato lo schema (`hasSchema`, ADR 0041): un «0 col.»\r\n * sarebbe un'affermazione che nessuno ha fatto;\r\n * - il nodo e' un **pozzo**: il conteggio risponde a «cosa esce di qui», e da un pozzo non esce\r\n * niente. Lo zero e' vero — e' la condizione normale di ogni writeback — ed e' la risposta a\r\n * una domanda che non si pone.\r\n *\r\n * Sui nodi **non** pozzo lo zero si mostra: li' significa `DPE_SCHEMA_EMPTY`, che e' un errore.\r\n */\r\n columnCount: number | null;\r\n}\r\n\r\nexport interface DpeCanvasEdge {\r\n id: string;\r\n sourceId: string;\r\n targetId: string;\r\n slot: string;\r\n}\r\n","/**\r\n * Il canvas del DAG.\r\n *\r\n * @foblex/flow in *classic mode*: la libreria disegna e riconosce i gesti, **lo stato resta\r\n * nostro**. Nodi e archi sono signal derivati da `DpeDocumentStore` attraverso\r\n * `core/dpe-graph.ts`, e ogni gesto e' una mutazione del documento.\r\n *\r\n * Cosa il canvas **non** fa: non decide se un collegamento sia lecito. Un ciclo (**I2**) o un\r\n * riferimento a valle (**I3**) li rifiuta il backend, e la diagnostica arriva subito perche' la\r\n * validazione parte dopo la mutazione. Disegnare solo cio' che il backend accetterebbe\r\n * richiederebbe di riprodurre qui la sua semantica: e' esattamente cio' che non facciamo.\r\n */\r\n\r\nimport {\r\n ChangeDetectionStrategy,\r\n Component,\r\n ElementRef,\r\n computed,\r\n effect,\r\n inject,\r\n input,\r\n output,\r\n signal,\r\n viewChild,\r\n} from '@angular/core';\r\nimport {\r\n EFConnectableSide,\r\n EFMarkerType,\r\n FCanvasComponent,\r\n FFlowComponent,\r\n FFlowModule,\r\n provideFFlow,\r\n withA11y,\r\n} from '@foblex/flow';\r\n\r\nimport { DpeDocumentStore } from '../../core/dpe-document.store';\r\nimport {\r\n dpeAllNodes,\r\n dpeInputPorts,\r\n dpeNodeId,\r\n dpeOutputPortId,\r\n dpeParseInputPortId,\r\n dpeParseNodeId,\r\n dpeParseOutputPortId,\r\n} from '../../core/dpe-graph';\r\nimport { dpeNodeCategory } from '../../core/dpe-node-catalog';\r\nimport { dpeIsNodeCollectionName } from '../../core/dpe-node-collection-guard';\r\nimport { dpeNodeIconFamilyClass } from '../../core/dpe-node-icons';\r\nimport { dpeIsRectFullyVisible } from '../../core/dpe-viewport';\r\nimport { dpeNodeSubtitle } from '../../core/dpe-node-factory';\r\nimport { DpeNodeCollectionName } from '../../model/dpe-definition.model';\r\nimport { DpeNodeIconComponent } from '../shared/dpe-node-icon.component';\r\nimport {\r\n DPE_NODE_HEIGHT,\r\n DpeCanvasEdge,\r\n DpeCanvasNode,\r\n DpeCanvasPort,\r\n dpeNodeWidth,\r\n dpeNodeWidthClass,\r\n} from './dpe-canvas.model';\r\n\r\n/** Etichette delle porte di ingresso, per slot. Presentazione, non semantica. */\r\nconst PORT_LABELS: Readonly<Record<string, string>> = {\r\n source: 'Ingresso',\r\n primary: 'Primaria',\r\n secondary: 'Secondaria',\r\n sources: 'Sorgente',\r\n};\r\n\r\n/**\r\n * Respiro attorno al grafo inquadrato. Era 100 per lato: su un'area alta 590 px erano 200 px di\r\n * vuoto, cioe' un terzo dello spazio, e bastavano a spingere l'inquadratura sotto la soglia di\r\n * leggibilita' — quindi a farla saltare del tutto.\r\n *\r\n * Il valore verticale e' un filo piu' generoso di quello orizzontale, e non per estetica: la card\r\n * disegna piu' del rettangolo con cui e' misurata (le porte sporgono sopra, il connettore di\r\n * uscita sotto), e con un respiro simmetrico l'ultima card restava tangente al bordo inferiore —\r\n * misurato: un pixel fuori.\r\n */\r\nconst FIT_PADDING = { x: 44, y: 56 };\r\n\r\n/**\r\n * Scala minima con cui il canvas si apre. Sotto questa soglia le etichette della card — nome,\r\n * categoria, conteggio delle colonne — sono disegnate in un corpo che non si legge, e si apre al\r\n * 100% centrato lasciando all'utente lo zoom (rotella, `+`/`-`, minimappa, «Inquadra»).\r\n *\r\n * Il valore non e' arbitrario: il titolo della card e' 13px, quindi a 0,75 resta un corpo effettivo\r\n * di ~10px, il minimo che si legge. Era stato abbassato a 0.35 per soddisfare il criterio «nessun\r\n * nodo fuori dal riquadro all'apertura», criterio poi **ritirato** dal committente\r\n * (`docs/frontend.md` §3-ter): su un grafo grande si soddisfa solo rimpicciolendo fino\r\n * all'illeggibile — misurato sul seed della demo, scala 0,516 e titolo effettivo 6,7px. La\r\n * leggibilita' viene prima della vista d'insieme; la vista d'insieme resta a un clic, tramite\r\n * «Inquadra», che in questo ramo si mostra apposta.\r\n */\r\nconst MIN_INITIAL_SCALE = 0.75;\r\n\r\n/**\r\n * Respiro richiesto attorno al nodo perche' si consideri \"in vista\". Un nodo tangente al bordo\r\n * e' visibile e illeggibile: la panoramica scatta anche in quel caso.\r\n */\r\nconst REVEAL_MARGIN = 24;\r\n\r\n@Component({\r\n selector: 'dpe-canvas',\r\n standalone: true,\r\n imports: [FFlowModule, DpeNodeIconComponent],\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n templateUrl: './dpe-canvas.component.html',\r\n /**\r\n * Il layer da tastiera di @foblex/flow e' **opzionale**: senza `withA11y()` la libreria non\r\n * ascolta nessun tasto e `(fDeleteSelected)` — l'unico modo in cui annuncia «l'utente vuole\r\n * cancellare» — non viene mai emesso. Nessun errore: semplicemente `Canc` non fa niente.\r\n * Attiva anche frecce, `Ctrl+A`, `Escape`, zoom da tastiera, cioe' la meta' della\r\n * navigazione senza mouse che un editor a canvas deve avere.\r\n *\r\n * Provider **del componente**: montare il canvas non installa ascoltatori globali nell'app\r\n * ospite.\r\n */\r\n providers: [provideFFlow(withA11y())],\r\n})\r\nexport class DpeCanvasComponent {\r\n private readonly store = inject(DpeDocumentStore);\r\n /** Serve per misurare l'area disponibile prima di decidere la scala d'apertura. */\r\n private readonly host = inject<ElementRef<HTMLElement>>(ElementRef);\r\n\r\n /** Marker \"all states\": senza, l'arco selezionato perde la punta. */\r\n readonly markerEnd = EFMarkerType.END_ALL_STATES;\r\n\r\n /**\r\n * Ingressi in alto, uscita in basso, **dichiarati**. Lasciando calcolare il lato, un arco\r\n * che risale (un nodo posizionato sopra la sua sorgente) esce di fianco e il grafo diventa\r\n * illeggibile.\r\n */\r\n readonly sides = EFConnectableSide;\r\n\r\n readonly isEditable = input(true);\r\n\r\n readonly nodeOpened = output<string>();\r\n readonly nodeDropped = output<{ collection: DpeNodeCollectionName; x: number; y: number }>();\r\n\r\n private readonly canvas = viewChild.required(FCanvasComponent);\r\n /**\r\n * Serve solo a **riallineare** la selezione della libreria a quella dello store: e' l'unico\r\n * punto che scrive nello stato di selezione di @foblex/flow.\r\n */\r\n private readonly flow = viewChild(FFlowComponent);\r\n private hasFramedOnce = false;\r\n\r\n /**\r\n * Visibilita' del comando «Inquadra». Non e' piu' «l'utente ha spostato la vista»: lo diventa\r\n * anche all'apertura quando l'editor rinuncia a inquadrare per non scendere sotto\r\n * `MIN_INITIAL_SCALE`. In quel caso la vista d'insieme non e' stata data, quindi il comando che\r\n * la offre deve essere raggiungibile — nasconderlo significherebbe toglierla e basta.\r\n */\r\n private readonly _isFitCommandVisible = signal(false);\r\n readonly isFitCommandVisible = this._isFitCommandVisible.asReadonly();\r\n\r\n readonly nodes = computed<DpeCanvasNode[]>(() => {\r\n const definition = this.store.definition();\r\n const positions = this.store.positions();\r\n const diagnostics = this.store.diagnosticsByNode();\r\n const schemas = this.store.schemaByNode();\r\n const known = new Set(this.store.graph().nodes.map((node) => node.name));\r\n\r\n // Si cammina la definizione, non il grafo: servono i nodi veri (etichetta, sintesi) e le\r\n // porte, che si leggono dagli slot del nodo.\r\n return dpeAllNodes(definition).map((located) => {\r\n const category = dpeNodeCategory(located.collection);\r\n const ports: DpeCanvasPort[] = dpeInputPorts(located).map((port) => ({\r\n id: port.id,\r\n slot: port.slot,\r\n index: port.index,\r\n label:\r\n port.index === undefined\r\n ? (PORT_LABELS[port.slot] ?? port.slot)\r\n : `${PORT_LABELS[port.slot] ?? port.slot} ${port.index + 1}`,\r\n sourceName: port.sourceName,\r\n // Un nome che non corrisponde a nessun nodo non e' disegnabile come arco — non\r\n // esiste il connettore da cui partirebbe — quindi si dice **sulla porta**. Nasconderlo\r\n // sarebbe l'unico modo di non vedere un riferimento rotto.\r\n isBroken: port.sourceName !== undefined && !known.has(port.sourceName),\r\n }));\r\n const name = located.node.name;\r\n const nodeDiagnostics = diagnostics.get(name);\r\n const schema = schemas.get(name);\r\n\r\n return {\r\n name,\r\n id: dpeNodeId(name),\r\n collection: located.collection,\r\n categoryLabel: category.label,\r\n label: located.node.label || name,\r\n subtitle: dpeNodeSubtitle(located.collection, located.node),\r\n position: positions.get(name) ?? { x: 0, y: 0 },\r\n ports,\r\n showsPortLabels: ports.length > 1,\r\n widthClass: dpeNodeWidthClass(ports.length),\r\n familyClass: dpeNodeIconFamilyClass(located.collection),\r\n outputPortId: dpeOutputPortId(name),\r\n hasOutput: !category.isSink,\r\n severity: nodeDiagnostics?.severity ?? null,\r\n diagnosticCount: nodeDiagnostics?.diagnostics.length ?? 0,\r\n // Due fonti concordi: la diagnostica del backend (autorevole) e lo schema, che porta\r\n // `isCompilable`. La categoria «non compilabile in v1» del catalogo e' solo un\r\n // suggerimento della palette e qui non basterebbe.\r\n isNotCompilable:\r\n (nodeDiagnostics?.isNotCompilable ?? false) || schema?.isCompilable === false,\r\n // Due condizioni perche' il numero compaia, e rispondono a due domande diverse.\r\n //\r\n // 1. Il backend ha davvero derivato lo schema (`hasSchema`, ADR 0041): a schema ignoto\r\n // `columns` e' vuota perche' la risposta non c'e', e «0 col.» affermerebbe un fatto che\r\n // nessuno ha affermato.\r\n // 2. Il nodo **non e' un pozzo**. Il conteggio risponde a «cosa esce di qui», e da un\r\n // pozzo non esce niente: lo zero e' la risposta a una domanda che non si pone. Ogni\r\n // writeback sano porta `hasSchema: true` con zero colonne — condizione **normale**\r\n // (ADR 0041 addendum 1), non un difetto — quindi il numero comparirebbe su ogni pozzo\r\n // di ogni definizione, sempre. Sui nodi non pozzo lo zero **resta**: li' e'\r\n // `DPE_SCHEMA_EMPTY`, cioe' un errore, ed e' informativo.\r\n //\r\n // La categoria e' la stessa che decide `hasOutput` qui sopra: nessun dato nuovo, nessuna\r\n // deduzione. `null` = nessuna pastiglia.\r\n // `schema.columns.length` senza ripiego: la collezione e' obbligatoria nel contratto, e un\r\n // `?? 0` avrebbe confuso «zero colonne» con «la collezione non e' arrivata» proprio nella\r\n // pastiglia che quello zero lo mostra.\r\n columnCount: !category.isSink && schema?.hasSchema === true ? schema.columns.length : null,\r\n };\r\n });\r\n });\r\n\r\n /**\r\n * Solo gli archi con **entrambi** i capi risolti diventano `<f-connection>`: un capo che non\r\n * esiste non ha un connettore, e una connessione verso un id inesistente si attacca a un\r\n * punto arbitrario del canvas. Il riferimento rotto si vede sulla porta del nodo.\r\n */\r\n readonly edges = computed<DpeCanvasEdge[]>(() =>\r\n this.store\r\n .graph()\r\n .edges.filter((edge) => edge.isResolved)\r\n .map((edge) => ({\r\n id: edge.id,\r\n sourceId: edge.fromPortId,\r\n targetId: edge.toPortId,\r\n slot: edge.slot,\r\n })),\r\n );\r\n\r\n constructor() {\r\n /*\r\n * Riallineamento della selezione: **una sola scrittura**, e in questa direzione.\r\n *\r\n * @foblex/flow tiene una sua selezione (classe `f-selected`, elenco interno usato da drag\r\n * multiplo e `Canc`) e la aggiorna da se' sui gesti. Se la si lascia autorevole si finisce\r\n * con due sistemi che non si parlano: il nodo raggiunto da una diagnostica porta la nostra\r\n * classe, quello cliccato prima porta `f-selected`, e l'utente vede due nodi selezionati\r\n * senza sapere su quale sta agendo. La cura non e' azzerare l'altro sistema dove serve — e'\r\n * derivarlo: il gesto entra da `fSelectionChange` e finisce nello store, lo store torna qui e\r\n * scrive nella libreria. La classe `dpe-node--selected` deriva dallo stesso stato.\r\n *\r\n * `isSelectedChanged: false` perche' questa scrittura **non** e' un gesto dell'utente e non\r\n * deve far emettere un cambio di selezione: sarebbe il ciclo.\r\n */\r\n effect(() => {\r\n const selected = this.store.selectedNodeNames();\r\n const flow = this.flow();\r\n if (!flow) {\r\n return;\r\n }\r\n if (selected.length === 0) {\r\n // `clearSelection` agisce subito; `select` no (vedi sotto), quindi il caso vuoto passa da qui.\r\n flow.clearSelection();\r\n return;\r\n }\r\n flow.select(selected.map((name) => dpeNodeId(name)), [], false);\r\n // `select()` della libreria non applica: si iscrive al prossimo cambio delle connessioni e\r\n // agisce **la' dentro**. Senza una notifica, la selezione programmatica resta in attesa\r\n // indefinitamente e nel DOM non compare niente — nessun errore, solo un `f-selected` che\r\n // non arriva. `redraw()` e' cio' che emette quel cambio.\r\n flow.redraw();\r\n });\r\n }\r\n\r\n // -------------------------------------------------------------------------\r\n // Navigazione\r\n // -------------------------------------------------------------------------\r\n\r\n /**\r\n * Porta il nodo in vista **muovendo la vista**, non il nodo: le coordinate del documento non si\r\n * toccano (una navigazione non e' una modifica). Se il nodo e' gia' interamente visibile non fa\r\n * nulla, perche' una panoramica non richiesta disorienta quanto la sua assenza.\r\n */\r\n revealNode(name: string): void {\r\n const id = dpeNodeId(name);\r\n const element = this.findNodeElement(id);\r\n if (!element) {\r\n return;\r\n }\r\n const view = this.host.nativeElement.getBoundingClientRect();\r\n if (dpeIsRectFullyVisible(element.getBoundingClientRect(), view, REVEAL_MARGIN)) {\r\n return;\r\n }\r\n // La panoramica e' un movimento della vista fatto da noi, ma resta un movimento: lasciando\r\n // emettere `fCanvasChange` compare «Inquadra», che e' il modo di tornare all'insieme.\r\n this.canvas().centerGroupOrNode(id, true);\r\n }\r\n\r\n /**\r\n * Il nodo nel DOM. Si scorre l'elenco invece di comporre un selettore con il valore\r\n * dell'attributo: il nome del nodo arriva dal documento e puo' contenere qualunque carattere,\r\n * e un selettore costruito per concatenazione su un dato del documento e' un errore in attesa.\r\n */\r\n private findNodeElement(id: string): HTMLElement | undefined {\r\n const elements = this.host.nativeElement.querySelectorAll<HTMLElement>('[data-f-node-id]');\r\n for (const element of Array.from(elements)) {\r\n if (element.getAttribute('data-f-node-id') === id) {\r\n return element;\r\n }\r\n }\r\n return undefined;\r\n }\r\n\r\n /** Misure per il layout automatico: le stesse che il CSS applica con le classi. */\r\n layoutInput(): { name: string; width: number; height: number }[] {\r\n return this.nodes().map((node) => ({\r\n name: node.name,\r\n width: dpeNodeWidth(node.ports.length),\r\n height: DPE_NODE_HEIGHT,\r\n }));\r\n }\r\n\r\n // -------------------------------------------------------------------------\r\n // Viewport\r\n // -------------------------------------------------------------------------\r\n\r\n /**\r\n * `fitToScreen` va chiamato **dopo** il render dei nodi: prima, la libreria non conosce\r\n * ancora le loro dimensioni e inquadra la vista sbagliata. Solo la prima volta: rifarlo\r\n * combatterebbe con l'utente che ha spostato la vista.\r\n */\r\n onNodesRendered(): void {\r\n if (this.hasFramedOnce || this.nodes().length < 2) {\r\n return;\r\n }\r\n /*\r\n * Si inquadra **dopo** che tutti i nodi hanno una posizione. Un documento che non porta\r\n * coordinate (ADR 0013) le riceve da dagre subito dopo il primo render: inquadrare prima\r\n * significava inquadrare un mucchio di card tutte in (0,0), e il risultato era il grafo\r\n * tagliato in alto a sinistra all'apertura — con `hasFramedOnce` gia' consumato, quindi senza\r\n * una seconda occasione. Qui non si consuma niente: il layout scrive le posizioni, i nodi si\r\n * ridisegnano e questo gestore torna con le misure vere.\r\n */\r\n const positions = this.store.positions();\r\n if (this.nodes().some((node) => !positions.has(node.name))) {\r\n return;\r\n }\r\n this.hasFramedOnce = true;\r\n const canvas = this.canvas();\r\n // La scala si **stima prima**: chiedere `getScale()` dopo `fitToScreen` restituisce la scala\r\n // di configurazione e non quella applicata, quindi il controllo non scattava mai e le card\r\n // restavano illeggibili. La stima usa gli stessi numeri che il CSS applica alle card.\r\n if (this.wouldFitBelowMinimumScale()) {\r\n // 100% centrato sui nodi: si perde la vista d'insieme e si guadagna un'etichetta\r\n // leggibile. `emitCanvasChange: false` perche' l'inquadratura iniziale non e' un gesto\r\n // dell'utente; il comando «Inquadra» si mostra qui in modo esplicito, perche' e' l'unico\r\n // modo di ottenere la panoramica a cui si e' appena rinunciato.\r\n canvas.resetScaleAndCenter(false, false);\r\n this._isFitCommandVisible.set(true);\r\n return;\r\n }\r\n canvas.fitToScreen(FIT_PADDING, false, false);\r\n }\r\n\r\n /** Vero quando inquadrare tutto il grafo richiederebbe una scala sotto la soglia leggibile. */\r\n private wouldFitBelowMinimumScale(): boolean {\r\n const nodes = this.nodes();\r\n if (nodes.length === 0) {\r\n return false;\r\n }\r\n let left = Number.POSITIVE_INFINITY;\r\n let top = Number.POSITIVE_INFINITY;\r\n let right = Number.NEGATIVE_INFINITY;\r\n let bottom = Number.NEGATIVE_INFINITY;\r\n for (const node of nodes) {\r\n const width = dpeNodeWidth(node.ports.length);\r\n left = Math.min(left, node.position.x);\r\n top = Math.min(top, node.position.y);\r\n right = Math.max(right, node.position.x + width);\r\n bottom = Math.max(bottom, node.position.y + DPE_NODE_HEIGHT);\r\n }\r\n const host = this.host.nativeElement;\r\n const availableWidth = host.clientWidth - FIT_PADDING.x * 2;\r\n const availableHeight = host.clientHeight - FIT_PADDING.y * 2;\r\n if (availableWidth <= 0 || availableHeight <= 0) {\r\n return false;\r\n }\r\n const scale = Math.min(\r\n availableWidth / Math.max(right - left, 1),\r\n availableHeight / Math.max(bottom - top, 1),\r\n );\r\n return scale < MIN_INITIAL_SCALE;\r\n }\r\n\r\n onCanvasChange(): void {\r\n this._isFitCommandVisible.set(true);\r\n }\r\n\r\n resetViewport(): void {\r\n this.canvas().fitToScreen(FIT_PADDING, true, false);\r\n // Il grafo e' inquadrato: il comando non ha piu' niente da offrire finche' la vista non si muove.\r\n this._isFitCommandVisible.set(false);\r\n }\r\n\r\n // -------------------------------------------------------------------------\r\n // Gesti\r\n // -------------------------------------------------------------------------\r\n\r\n /**\r\n * Creazione di un arco: si scrive il **riferimento** sulla porta di destinazione. La\r\n * direzione del gesto e' irrilevante — cio' che conta e' quale slot riceve il nome, ed e'\r\n * per questo che le porte di ingresso sono una per slot.\r\n */\r\n onCreateConnection(event: { sourceId: string; targetId?: string }): void {\r\n if (!this.isEditable() || !event.targetId) {\r\n return;\r\n }\r\n const source = dpeParseOutputPortId(event.sourceId);\r\n const target = dpeParseInputPortId(event.targetId);\r\n if (!source || !target || source === target.nodeName) {\r\n // Un nodo che si collega a se stesso e' un ciclo di lunghezza uno: il backend lo\r\n // rifiuterebbe con `DPE_GRAPH_CYCLE`, e disegnarlo non aggiunge informazione.\r\n return;\r\n }\r\n this.store.connect(target.nodeName, target.slot, source, target.index);\r\n }\r\n\r\n /** Riassegnazione di un arco gia' disegnato, da un capo o dall'altro. */\r\n onReassignConnection(event: {\r\n endpoint: string;\r\n previousSourceId: string;\r\n nextSourceId?: string;\r\n previousTargetId: string;\r\n nextTargetId?: string;\r\n }): void {\r\n if (!this.isEditable()) {\r\n return;\r\n }\r\n const previousTarget = dpeParseInputPortId(event.previousTargetId);\r\n const previousSource = dpeParseOutputPortId(event.previousSourceId);\r\n if (!previousTarget || !previousSource) {\r\n return;\r\n }\r\n if (event.endpoint === 'target') {\r\n const nextTarget = event.nextTargetId ? dpeParseInputPortId(event.nextTargetId) : undefined;\r\n this.store.disconnect(previousTarget.nodeName, previousTarget.slot, previousTarget.index);\r\n if (nextTarget) {\r\n this.store.connect(nextTarget.nodeName, nextTarget.slot, previousSource, nextTarget.index);\r\n }\r\n return;\r\n }\r\n const nextSource = event.nextSourceId ? dpeParseOutputPortId(event.nextSourceId) : undefined;\r\n if (nextSource) {\r\n this.store.connect(previousTarget.nodeName, previousTarget.slot, nextSource, previousTarget.index);\r\n }\r\n }\r\n\r\n /**\r\n * Spostamento: un solo passo di cronologia per l'intero gesto. Le posizioni intermedie non\r\n * sono modifiche che l'utente vuole annullare una per una.\r\n */\r\n onMoveNodes(event: { nodes: Array<{ id: string; position: { x: number; y: number } }> }): void {\r\n event.nodes.forEach((moved, index) => {\r\n const name = dpeParseNodeId(moved.id);\r\n if (name) {\r\n this.store.moveNode(name, moved.position, { silent: index > 0 });\r\n }\r\n });\r\n }\r\n\r\n /**\r\n * Il gesto di selezione della libreria entra **nello store** e non altrove: e' l'unico verso in\r\n * cui la selezione di @foblex/flow conta, come rilevazione del gesto. Cio' che si vede lo decide\r\n * lo store, attraverso l'effect del costruttore.\r\n */\r\n onSelectionChange(event: { nodeIds: string[] }): void {\r\n const names: string[] = [];\r\n for (const id of event.nodeIds) {\r\n const name = dpeParseNodeId(id);\r\n if (name) {\r\n names.push(name);\r\n }\r\n }\r\n this.store.selectMany(names);\r\n }\r\n\r\n onDeleteSelected(event: { nodeIds: string[] }): void {\r\n if (!this.isEditable()) {\r\n return;\r\n }\r\n for (const id of event.nodeIds) {\r\n const name = dpeParseNodeId(id);\r\n if (name) {\r\n this.store.removeNode(name);\r\n }\r\n }\r\n }\r\n\r\n /**\r\n * Rilascio di una voce trascinata dalla palette. `externalItemRect` e' gia' nel sistema di\r\n * coordinate del canvas: convertirlo a mano dal viewport sarebbe rifare la trasformazione\r\n * di zoom e panoramica della libreria.\r\n *\r\n * `data` e' `unknown` per @foblex/flow, e qui il dato si **controlla** invece di convertirlo con\r\n * un `as`: un rilascio che non porta una collezione di nodi si ignora, come si ignorava un dato\r\n * vuoto. E' il confine che permette a `dpeNodeCategory` di lanciare fuori dall'unione.\r\n */\r\n onCreateNode(event: { data: unknown; externalItemRect: { x: number; y: number } }): void {\r\n if (!this.isEditable()) {\r\n return;\r\n }\r\n const collection = event.data;\r\n if (!dpeIsNodeCollectionName(collection)) {\r\n return;\r\n }\r\n this.nodeDropped.emit({\r\n collection,\r\n x: Math.round(event.externalItemRect.x),\r\n y: Math.round(event.externalItemRect.y),\r\n });\r\n }\r\n\r\n onNodeDoubleClick(name: string): void {\r\n this.nodeOpened.emit(name);\r\n }\r\n\r\n onEditClick(event: Event, name: string): void {\r\n // Senza `stopPropagation` il clic vale anche come selezione dell'area sotto il bottone.\r\n event.stopPropagation();\r\n this.nodeOpened.emit(name);\r\n }\r\n\r\n onRemoveClick(event: Event, name: string): void {\r\n event.stopPropagation();\r\n this.store.removeNode(name);\r\n }\r\n\r\n /** La classe nel DOM **deriva** dallo store: non e' una seconda registrazione della selezione. */\r\n isSelected(node: DpeCanvasNode): boolean {\r\n return this.store.selectedNodeNameSet().has(node.name);\r\n }\r\n}\r\n","<!--\r\n Gerarchia obbligatoria: f-flow > f-canvas > fNode / f-connection. I `@for` stanno\r\n direttamente dentro <f-canvas>: senza wrapper non serve `ngProjectAs`.\r\n-->\r\n<!--\r\n `dpe-flow`: classe nostra sul contenitore della libreria, e serve al **CSS**. Il tema di\r\n @foblex/flow stila la card con `.f-flow .f-node` (due classi): un nostro `.dpe-node` non la\r\n batte, e senza questa classe la card torna larga 120px e col testo centrato. Con `dpe-flow` i\r\n selettori del foglio diventano `dpe-canvas .dpe-flow .dpe-node`, che vince per specificita' e\r\n non per ordine di caricamento.\r\n-->\r\n<f-flow\r\n class=\"dpe-flow\"\r\n fDraggable\r\n (fCreateConnection)=\"onCreateConnection($event)\"\r\n (fReassignConnection)=\"onReassignConnection($event)\"\r\n (fMoveNodes)=\"onMoveNodes($event)\"\r\n (fSelectionChange)=\"onSelectionChange($event)\"\r\n (fDeleteSelected)=\"onDeleteSelected($event)\"\r\n (fCreateNode)=\"onCreateNode($event)\"\r\n (fNodesRendered)=\"onNodesRendered()\"\r\n>\r\n <f-canvas fZoom #canvas (fCanvasChange)=\"onCanvasChange()\">\r\n <f-background>\r\n <f-circle-pattern />\r\n </f-background>\r\n\r\n @for (edge of edges(); track edge.id) {\r\n <!--\r\n La classe porta lo **slot** di destinazione: la primaria e la secondaria di un join\r\n vanno distinte a vista, altrimenti l’unico modo di sapere quale lato e’ quale e’ aprire\r\n l’ispettore. I colori stanno nel CSS globale, non qui: i path SVG sono creati a runtime\r\n e non portano l’attributo di scope.\r\n -->\r\n <f-connection\r\n [fConnectionId]=\"edge.id\"\r\n [fSourceId]=\"edge.sourceId\"\r\n [fTargetId]=\"edge.targetId\"\r\n fBehavior=\"floating\"\r\n fType=\"bezier\"\r\n [class]=\"'dpe-edge dpe-edge--' + edge.slot\"\r\n >\r\n <f-connection-marker-arrow [type]=\"markerEnd\" />\r\n </f-connection>\r\n }\r\n\r\n @for (node of nodes(); track node.id) {\r\n <!--\r\n Nessun `[style.*]` sul nodo: @foblex/flow riscrive l’attributo `style` a ogni\r\n riposizionamento e lo cancellerebbe. La larghezza arriva da `node.widthClass`.\r\n -->\r\n <div\r\n fNode\r\n fDragHandle\r\n [fNodeId]=\"node.id\"\r\n [fNodePosition]=\"node.position\"\r\n [class]=\"\r\n 'dpe-node dpe-node--' + node.collection + ' ' + node.widthClass + ' ' + node.familyClass\r\n \"\r\n [class.dpe-node--selected]=\"isSelected(node)\"\r\n [class.dpe-node--error]=\"node.severity === 'Error'\"\r\n [class.dpe-node--warning]=\"node.severity === 'Warning'\"\r\n [class.dpe-node--not-compilable]=\"node.isNotCompilable\"\r\n [attr.aria-label]=\"node.categoryLabel + ' ' + node.label\"\r\n (dblclick)=\"onNodeDoubleClick(node.name)\"\r\n >\r\n <!--\r\n Le porte di ingresso stanno in fila sul bordo superiore, una per slot. Si renderizzano\r\n **sempre**: un connettore con `display: none` ha geometria 0x0 e l’arco si attacca nel\r\n punto sbagliato. `fConnectorConnectableSide` e’ dichiarato: senza, un arco che risale\r\n entrerebbe di fianco.\r\n -->\r\n @if (node.ports.length > 0) {\r\n <div class=\"dpe-node__ports\" [class.dpe-node__ports--labelled]=\"node.showsPortLabels\">\r\n @for (port of node.ports; track port.id) {\r\n <div class=\"dpe-port\">\r\n <div\r\n fConnector\r\n fConnectorType=\"target\"\r\n [fConnectorId]=\"port.id\"\r\n [fConnectorConnectableSide]=\"sides.TOP\"\r\n fConnectorMultiple=\"false\"\r\n class=\"dpe-connector dpe-connector--in\"\r\n [class.dpe-connector--broken]=\"port.isBroken\"\r\n [title]=\"port.label\"\r\n ></div>\r\n @if (node.showsPortLabels) {\r\n <span class=\"dpe-port__label\" [title]=\"port.label\">{{ port.label }}</span>\r\n }\r\n </div>\r\n }\r\n </div>\r\n }\r\n\r\n <div class=\"dpe-node__head\">\r\n <!--\r\n La stessa pastiglia della palette: cio’ che si trascina deve assomigliare a cio’ che\r\n compare. La classe di famiglia porta i due token di colore.\r\n -->\r\n <span class=\"dpe-node__icon\">\r\n <dpe-node-icon [collection]=\"node.collection\" />\r\n </span>\r\n <div class=\"dpe-node__text\">\r\n <span class=\"dpe-node__title\" [title]=\"node.label\">{{ node.label }}</span>\r\n <span class=\"dpe-node__sub\">\r\n {{ node.categoryLabel }}\r\n @if (node.subtitle) {\r\n <span class=\"dpe-node__dot\">·</span>{{ node.subtitle }}\r\n }\r\n </span>\r\n </div>\r\n <!--\r\n `fDragBlocker` sui bottoni dentro un nodo: senza, il pointerdown diventa un\r\n trascinamento e aprire l’ispettore sposta il nodo di qualche pixel.\r\n -->\r\n <button\r\n type=\"button\"\r\n fDragBlocker\r\n class=\"dpe-node__btn\"\r\n [attr.aria-label]=\"'Apri l’ispettore di ' + node.label\"\r\n title=\"Apri l’ispettore\"\r\n (click)=\"onEditClick($event, node.name)\"\r\n >\r\n ✎\r\n </button>\r\n @if (isEditable() && isSelected(node)) {\r\n <!--\r\n Solo sul nodo selezionato: un comando distruttivo che compare sotto il puntatore\r\n mentre si attraversa il grafo si preme per sbaglio.\r\n -->\r\n <button\r\n type=\"button\"\r\n fDragBlocker\r\n class=\"dpe-node__btn dpe-node__btn--remove\"\r\n [attr.aria-label]=\"'Elimina ' + node.label\"\r\n title=\"Elimina (si annulla con ↶)\"\r\n (click)=\"onRemoveClick($event, node.name)\"\r\n >\r\n ×\r\n </button>\r\n }\r\n </div>\r\n\r\n <div class=\"dpe-node__meta\">\r\n <span class=\"dpe-node__name\" [title]=\"'Nome tecnico: ' + node.name\">{{ node.name }}</span>\r\n @if (node.columnCount !== null) {\r\n <span class=\"dpe-badge\" [title]=\"'Colonne in uscita secondo il backend'\">\r\n {{ node.columnCount }} col.\r\n </span>\r\n }\r\n @if (node.isNotCompilable) {\r\n <!--\r\n Stato distinto dall’errore: la definizione va bene, il compilatore non ci arriva\r\n (I10). Colore proprio, testo che non chiede di correggere niente.\r\n -->\r\n <span class=\"dpe-badge dpe-badge--not-compilable\" title=\"Valido, ma non compilabile: e’ un limite nostro\">\r\n non compilabile\r\n </span>\r\n }\r\n @if (node.diagnosticCount > 0) {\r\n <span\r\n class=\"dpe-badge\"\r\n [class.dpe-badge--error]=\"node.severity === 'Error'\"\r\n [class.dpe-badge--warning]=\"node.severity === 'Warning'\"\r\n [class.dpe-badge--info]=\"node.severity === 'Info'\"\r\n [title]=\"node.diagnosticCount + ' rilievi dal backend'\"\r\n >\r\n {{ node.diagnosticCount }}\r\n </span>\r\n }\r\n @for (port of node.ports; track $index) {\r\n @if (port.isBroken) {\r\n <span class=\"dpe-badge dpe-badge--error\" [title]=\"'Riferimento non risolto: ' + port.sourceName\">\r\n ⚠ {{ port.sourceName }}\r\n </span>\r\n }\r\n }\r\n </div>\r\n\r\n <!--\r\n L’uscita e’ una sola: lo schema prodotto e’ uno. I sink non ne hanno, e la porta non\r\n si disegna affatto — qui non c’e’ niente da nascondere con `display: none`, il\r\n connettore semplicemente non esiste.\r\n -->\r\n @if (node.hasOutput) {\r\n <div class=\"dpe-node__out\">\r\n <div\r\n fConnector\r\n fConnectorType=\"source\"\r\n [fConnectorId]=\"node.outputPortId\"\r\n [fConnectorConnectableSide]=\"sides.BOTTOM\"\r\n class=\"dpe-connector dpe-connector--out\"\r\n title=\"Uscita\"\r\n ></div>\r\n </div>\r\n }\r\n </div>\r\n }\r\n\r\n <f-connection-for-create fBehavior=\"floating\" fType=\"bezier\" class=\"dpe-edge dpe-edge--creating\">\r\n <f-connection-marker-arrow [type]=\"markerEnd\" />\r\n </f-connection-for-create>\r\n\r\n <f-selection-area />\r\n </f-canvas>\r\n\r\n <!--\r\n Fuori da <f-canvas> come la minimappa: dentro, la trasformazione del canvas se lo\r\n porterebbe via insieme ai nodi.\r\n -->\r\n <button\r\n type=\"button\"\r\n fDragBlocker\r\n class=\"dpe-viewport-reset\"\r\n [class.dpe-viewport-reset--hidden]=\"!isFitCommandVisible()\"\r\n title=\"Rimetti il grafo interamente in vista\"\r\n (click)=\"resetViewport()\"\r\n >\r\n ⤢ Inquadra\r\n </button>\r\n\r\n <f-minimap [fMinSize]=\"1200\" class=\"dpe-minimap\" />\r\n</f-flow>\r\n","/**\r\n * Il pannello delle diagnostiche.\r\n *\r\n * Mostra l'**elenco completo** (la validazione e' esaustiva, **I6**): non si accorpa, non si\r\n * tronca, non si riordina per gravita' — l'ordine del backend segue la struttura del documento e\r\n * perderlo rende impossibile capire quale nodo ha quale problema.\r\n *\r\n * La distinzione che questo pannello esiste per rendere: **nodo non valido** (l'utente deve\r\n * correggere) contro **nodo valido ma non ancora compilabile** (`DPE_NODE_NOT_COMPILABLE`,\r\n * **I10**). Sono situazioni diverse e hanno colore, icona ed etichetta diversi; mescolarle\r\n * significherebbe chiedere all'utente di correggere un limite nostro.\r\n *\r\n * Terza categoria a se': i rilievi **del dialetto** (`DPE_DIALECT_*`). Lo stesso documento su un\r\n * altro dialetto sarebbe compilabile, e l'interfaccia deve dirlo, altrimenti si cerca l'errore\r\n * nella definizione.\r\n */\r\n\r\nimport { ChangeDetectionStrategy, Component, computed, inject, output } from '@angular/core';\r\n\r\nimport { DpeDocumentStore } from '../../core/dpe-document.store';\r\nimport {\r\n dpeDiagnosticLocationLabel,\r\n dpeDiagnosticNodeName,\r\n dpeIsDialectDiagnostic,\r\n dpeIsNotCompilable,\r\n} from '../../core/dpe-diagnostics.util';\r\nimport { DPE_DIALECT_LABELS } from '../../core/dpe-node-catalog';\r\nimport { Diagnostic } from '../../model/dpe-diagnostics.model';\r\n\r\ninterface DpeDiagnosticRow {\r\n diagnostic: Diagnostic;\r\n location: string;\r\n /** Nodo a cui navigare, se il percorso permette di ricavarlo. */\r\n nodeName?: string;\r\n field?: string;\r\n isNotCompilable: boolean;\r\n isDialect: boolean;\r\n}\r\n\r\n@Component({\r\n selector: 'dpe-diagnostics-panel',\r\n standalone: true,\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n template: `\r\n <div class=\"dpe-diag__head\">\r\n <strong>Diagnostiche</strong>\r\n <span class=\"dpe-diag__count\">\r\n {{ counts().errors }} errori · {{ counts().warnings }} avvisi · {{ counts().infos }} info\r\n @if (counts().notCompilable > 0) {\r\n · {{ counts().notCompilable }} non compilabili\r\n }\r\n </span>\r\n </div>\r\n\r\n @if (rows().length === 0) {\r\n <p class=\"dpe-diag__empty\">\r\n @if (hasResult()) {\r\n Nessun rilievo dal backend.\r\n } @else {\r\n In attesa della prima validazione…\r\n }\r\n </p>\r\n }\r\n\r\n <!--\r\n track $index: le chiavi composte da codice e percorso si duplicano davvero (lo stesso\r\n codice sullo stesso campo di due righe di una collezione) e Angular lo tratta come errore.\r\n -->\r\n @for (row of rows(); track $index) {\r\n <!-- Una sola sorgente di classi: class statico e [class] si contenderebbero l’attributo. -->\r\n <button\r\n type=\"button\"\r\n [class]=\"'dpe-diag__row dpe-diag__row--' + row.diagnostic.severity\"\r\n [class.dpe-diag__row--not-compilable]=\"row.isNotCompilable\"\r\n [disabled]=\"!row.nodeName\"\r\n (click)=\"navigateTo(row)\"\r\n >\r\n <span class=\"dpe-diag__code\">{{ row.diagnostic.code }}</span>\r\n <span class=\"dpe-diag__text\">\r\n {{ row.diagnostic.message }}\r\n <br />\r\n <span class=\"dpe-diag__where\">{{ row.location }}</span>\r\n </span>\r\n @if (row.isNotCompilable) {\r\n <span\r\n class=\"dpe-diag__tag dpe-diag__tag--not-compilable\"\r\n title=\"Valido, ma il compilatore non lo traduce: non c’e’ nulla da correggere\"\r\n >\r\n limite nostro\r\n </span>\r\n }\r\n @if (row.isDialect) {\r\n <span\r\n class=\"dpe-diag__tag dpe-diag__tag--dialect\"\r\n [title]=\"'Dipende dalle capacita’ del dialetto ' + dialectLabel() + ': su un altro dialetto lo stesso documento potrebbe compilare'\"\r\n >\r\n {{ dialectLabel() }}\r\n </span>\r\n }\r\n </button>\r\n }\r\n `,\r\n})\r\nexport class DpeDiagnosticsPanelComponent {\r\n private readonly store = inject(DpeDocumentStore);\r\n\r\n /** Emesso quando la riga porta a un nodo: chi ascolta apre l'ispettore e inquadra il nodo. */\r\n readonly navigate = output<{ nodeName: string; field?: string }>();\r\n\r\n readonly hasResult = computed(() => this.store.validateResult() !== undefined);\r\n\r\n readonly dialectLabel = computed(() => {\r\n const dialect = this.store.definition().dialect;\r\n return DPE_DIALECT_LABELS[dialect] ?? dialect;\r\n });\r\n\r\n readonly rows = computed<DpeDiagnosticRow[]>(() => {\r\n const definition = this.store.definition();\r\n return this.store.diagnostics().map((diagnostic) => ({\r\n diagnostic,\r\n location: dpeDiagnosticLocationLabel(diagnostic, definition),\r\n nodeName: dpeDiagnosticNodeName(diagnostic, definition),\r\n field: diagnostic.path?.field,\r\n isNotCompilable: dpeIsNotCompilable(diagnostic),\r\n isDialect: dpeIsDialectDiagnostic(diagnostic),\r\n }));\r\n });\r\n\r\n readonly counts = computed(() => {\r\n let errors = 0;\r\n let warnings = 0;\r\n let infos = 0;\r\n let notCompilable = 0;\r\n for (const row of this.rows()) {\r\n if (row.isNotCompilable) {\r\n notCompilable += 1;\r\n }\r\n switch (row.diagnostic.severity) {\r\n case 'Error':\r\n errors += 1;\r\n break;\r\n case 'Warning':\r\n warnings += 1;\r\n break;\r\n default:\r\n infos += 1;\r\n break;\r\n }\r\n }\r\n return { errors, warnings, infos, notCompilable };\r\n });\r\n\r\n /**\r\n * Emette **soltanto** l'intenzione: chi la riceve seleziona e inquadra. Selezionare anche da qui\r\n * significherebbe due scritture della selezione per un solo clic — la strada per due nodi\r\n * selezionati insieme — e un pannello che pilota il canvas invece di chiederglielo.\r\n */\r\n protected navigateTo(row: DpeDiagnosticRow): void {\r\n if (!row.nodeName) {\r\n return;\r\n }\r\n this.navigate.emit({ nodeName: row.nodeName, field: row.field });\r\n }\r\n}\r\n","/**\r\n * I **fatti dal backend** su un nodo: schema in uscita e rilievi che lo riguardano.\r\n *\r\n * Sono in un componente a se' perche' nella dialog di modifica (ADR 0026) non stanno dove stanno\r\n * i campi: i campi prendono la colonna larga, questi la colonna stretta di destra. Metterli **di\r\n * fianco** e non sopra il form evita in anticipo il muro di avvisi che spinge il primo campo\r\n * fuori dallo schermo — con i rilievi in colonna il muro non si forma, quindi si mostrano\r\n * **tutti** (**I6**: l'elenco della validazione e' esaustivo, e troncarlo sarebbe mentire).\r\n *\r\n * In testa sta lo stato **valido ma non compilabile** (**I10**), perche' e' il fatto che spiega gli\r\n * altri due: senza di esso una colonna che elenca i rilievi tace proprio sul motivo per cui da\r\n * questo nodo non uscira' nessun SQL. «Non compilabile» e «non valido» sono due significati\r\n * distinti, e l'editor li tiene distinti per decisione (ADR 0011).\r\n *\r\n * Non calcola niente. Lo schema arriva da `validate` (**I4**: la propagazione e' del backend) e i\r\n * rilievi arrivano gia' raggruppati per nodo: qui si legge e si rende.\r\n *\r\n * **Un pannello di fatti riporta fatti; le cause le dicono le diagnostiche.** E' la regola che ha\r\n * rimesso in ordine la sezione dello schema, e vale oltre questo pannello. Qui a colonne zero si\r\n * leggeva «il backend non ha (ancora) propagato uno schema per questo nodo»: una **causa** dedotta\r\n * da un conteggio, falsa su ogni writeback sano — dove lo schema **e'** propagato e le colonne sono\r\n * zero per costruzione — e vera per caso solo sul nodo a schema ignoto. Tre stati erano collassati\r\n * in una frase.\r\n */\r\n\r\nimport { ChangeDetectionStrategy, Component, computed, inject, input } from '@angular/core';\r\n\r\nimport { dpeDiagnosticField } from '../../core/dpe-diagnostics.util';\r\nimport { DpeDocumentStore } from '../../core/dpe-document.store';\r\nimport { dpeFindNode } from '../../core/dpe-graph';\r\nimport { dpeNodeCategory } from '../../core/dpe-node-catalog';\r\nimport { Diagnostic } from '../../model/dpe-diagnostics.model';\r\n\r\n@Component({\r\n selector: 'dpe-node-facts',\r\n standalone: true,\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n template: `\r\n @if (isNotCompilable()) {\r\n <!--\r\n Tono deliberatamente **non** da errore: colore proprio (ne’ rosso ne’ giallo) e le stesse\r\n parole degli ispettori di Forecast e CustomNode, perche’ e’ lo stesso fatto detto nello\r\n stesso linguaggio. Chiedere di correggere qualcosa qui sarebbe una bugia.\r\n -->\r\n <p class=\"dpe-note dpe-note--not-compilable\">\r\n <span class=\"dpe-badge dpe-badge--not-compilable\">non compilabile</span>\r\n Il nodo e’ <strong>valido</strong>: non c’e’ niente da correggere. Il compilatore non lo\r\n traduce in questa versione, quindi per questo nodo\r\n <strong>non verra’ generato SQL</strong>.\r\n </p>\r\n }\r\n\r\n <section class=\"dpe-section\">\r\n <h4 class=\"dpe-section__title\">Schema in uscita</h4>\r\n <!--\r\n **Colonne da mostrare: si mostrano, e non si chiede altro.** Restano prima di tutto perche’\r\n sono cio’ che il backend ha mandato: un elenco non vuoto non si nasconde in nome di un\r\n booleano, nemmeno se i due si contraddicono. Sotto, i tre stati in cui non c’e’ nessun\r\n elenco.\r\n -->\r\n @if (columns().length > 0) {\r\n <div class=\"dpe-columns\">\r\n @for (column of columns(); track $index) {\r\n <span\r\n class=\"dpe-column\"\r\n [class.dpe-column--nullable]=\"column.isNullable\"\r\n [title]=\"column.type.kind + (column.isNullable ? ' · nullabile' : ' · non nulla')\"\r\n >\r\n {{ column.name }}\r\n </span>\r\n }\r\n </div>\r\n <p class=\"dpe-field__hint\">\r\n Tratteggiate le colonne nullabili. Lo schema arriva da <code>validate</code>: non lo\r\n calcoliamo qui.\r\n </p>\r\n } @else {\r\n <!--\r\n Nessun elenco: **tre stati, tre frasi**. Prima era una frase sola, e la frase era vera in\r\n uno solo dei tre — per caso. Su ogni writeback sano lo schema **e’** propagato e le\r\n colonne sono zero, quindi «il backend non ha propagato uno schema» diceva il falso\r\n proprio nel caso piu’ comune.\r\n\r\n La regola che divide i rami e’ quella del pannello schema e del canvas: si legge\r\n hasSchema, non si deduce dal numero di colonne.\r\n -->\r\n @if (!hasSchema()) {\r\n <!--\r\n L’unico ramo in cui «il backend non l’ha propagato» e’ vero: la derivazione non e’\r\n arrivata a una risposta, e l’elenco vuoto non afferma zero colonne (ADR 0041).\r\n\r\n Sta per primo perche’ **vince sul pozzo**: un writeback a valle di un ciclo non viene\r\n mai processato, e li’ non e’ che sappiamo che non ha colonne — e’ che non ci siamo\r\n arrivati.\r\n -->\r\n <p class=\"dpe-empty\">\r\n Schema ignoto: il backend non ha derivato uno schema per questo nodo.\r\n </p>\r\n } @else if (isSink()) {\r\n <!--\r\n Un pozzo ha zero colonne in uscita **per costruzione**, ed e’ un fatto vero, non\r\n un’ignoranza (ADR 0041 addendum 1). Non si parla di colonne mancanti: non manca niente.\r\n Stessa ragione per cui il canvas non ne mostra il conteggio.\r\n -->\r\n <p class=\"dpe-empty\">Un nodo pozzo non ha uno schema in uscita.</p>\r\n } @else {\r\n <!--\r\n Il fatto e basta. La **causa** e’ DPE_SCHEMA_EMPTY, che e’ un errore e che la\r\n diagnostica qui sotto spiega gia’: scriverla anche qui sarebbe una seconda copia di un\r\n fatto autorevole, e la seconda copia si scosta (ADR 0018).\r\n -->\r\n <p class=\"dpe-empty\">Nessuna colonna in uscita.</p>\r\n }\r\n }\r\n </section>\r\n\r\n @if (diagnostics().length > 0) {\r\n <section class=\"dpe-section\">\r\n <h4 class=\"dpe-section__title\">Rilievi su questo nodo</h4>\r\n @for (diagnostic of diagnostics(); track $index) {\r\n <p\r\n class=\"dpe-field__issue\"\r\n [class.dpe-field__issue--warning]=\"diagnostic.severity === 'Warning'\"\r\n [class.dpe-field__issue--info]=\"diagnostic.severity === 'Info'\"\r\n >\r\n <!--\r\n Il campo accanto al codice: due violazioni sullo stesso nodo sono **due** diagnostiche\r\n su due percorsi, e senza questo pezzo diventano due righe identiche a schermo. Il\r\n messaggio del contratto e’ deliberatamente uguale nei due casi — dice che il campo non\r\n e’ ammesso, non quale — quindi qui il campo e’ l’unica cosa che le distingue.\r\n -->\r\n <code>{{ diagnostic.code }}</code\r\n >@if (fieldOf(diagnostic); as campo) {\r\n · <code>{{ campo }}</code>\r\n }\r\n — {{ diagnostic.message }}\r\n </p>\r\n }\r\n </section>\r\n }\r\n `,\r\n})\r\nexport class DpeNodeFactsComponent {\r\n private readonly store = inject(DpeDocumentStore);\r\n\r\n /**\r\n * Il nodo di cui si mostrano i fatti. Arriva per input e non dalla selezione: chi monta questo\r\n * componente sa quale nodo sta guardando, e leggere qui la selezione lo farebbe cambiare\r\n * bersaglio sotto la dialog che lo contiene.\r\n */\r\n readonly nodeName = input.required<string>();\r\n\r\n /**\r\n * Il nodo e' valido ma **non compilabile** (**I10**). Due rami, ed e' la **stessa espressione**\r\n * che `DpeNodeInspectorBase.isNotCompilable` usa gia': il codice diagnostico\r\n * `DPE_NODE_NOT_COMPILABLE` raggruppato per nodo, **oppure** lo schema che dichiara\r\n * `isCompilable: false`. Servono entrambi perche' il backend puo' dirlo per due vie — un rilievo\r\n * sul nodo, o la riga di schema — e riconoscerne una sola vorrebbe dire tacere nell'altro caso.\r\n *\r\n * Qui non si giudica niente: la decisione e' del compilatore, questa e' la sua risposta.\r\n */\r\n readonly isNotCompilable = computed(\r\n () =>\r\n (this.store.diagnosticsByNode().get(this.nodeName())?.isNotCompilable ?? false) ||\r\n this.store.schemaByNode().get(this.nodeName())?.isCompilable === false,\r\n );\r\n\r\n readonly columns = computed(() => this.store.schemaByNode().get(this.nodeName())?.columns ?? []);\r\n\r\n /**\r\n * Se `columns` e' un **fatto** (ADR 0041). E' l'unica cosa che `hasSchema` dice: non dice che il\r\n * nodo sta bene, non dice che ha colonne, non dice che e' compilabile.\r\n *\r\n * `=== true` e non un confronto lasco: assente significa `false`, perche' un ospite che non\r\n * serializza i booleani falsi non manda affatto la proprieta' — e il verso prudente e' «non lo\r\n * so», non «zero colonne».\r\n */\r\n readonly hasSchema = computed(\r\n () => this.store.schemaByNode().get(this.nodeName())?.hasSchema === true,\r\n );\r\n\r\n /**\r\n * Il nodo e' un **pozzo**. Non si deduce da niente: si legge la collezione di appartenenza dal\r\n * documento e si chiede la categoria al catalogo dei nodi — la stessa che il view model del\r\n * canvas usa per decidere se disegnare la porta di uscita, e per la stessa domanda («da qui esce\r\n * qualcosa?»).\r\n *\r\n * Serve perche' uno schema di uscita **vuoto** significa due cose diverse su un pozzo e su un\r\n * nodo di mezzo, e la differenza non e' nei dati: e' nel tipo di nodo, che il backend ci ha gia'\r\n * detto qual e'.\r\n */\r\n readonly isSink = computed(() => {\r\n const located = dpeFindNode(this.store.definition(), this.nodeName());\r\n return located ? dpeNodeCategory(located.collection).isSink : false;\r\n });\r\n\r\n readonly diagnostics = computed(\r\n () => this.store.diagnosticsByNode().get(this.nodeName())?.diagnostics ?? [],\r\n );\r\n\r\n /**\r\n * Il campo in causa, dalla stessa funzione che gli ispettori usano per agganciare il rilievo al\r\n * controllo (`contracts.md` §1). Vuoto quando la diagnostica riguarda il nodo intero, e allora\r\n * la riga non mostra niente: e' un fatto sul nodo, non su un campo.\r\n */\r\n protected fieldOf(diagnostic: Diagnostic): string | undefined {\r\n return dpeDiagnosticField(diagnostic);\r\n }\r\n}\r\n","/**\r\n * `[dpeValue]` su un `<select>`.\r\n *\r\n * Perche' esiste, e non e' un vezzo: un `[value]` viene applicato **prima** che le `<option>`\r\n * generate da un `@for` esistano. Il browser scarta un valore che non corrisponde a nessuna\r\n * opzione, Angular non lo riapplica (il binding non e' cambiato) e la tendina resta sulla prima\r\n * voce — mostrando un valore diverso da quello del documento. Nessun errore, nessun avviso: solo\r\n * una tendina che mente. L'abbiamo visto sul selettore del dialetto e sulle funzioni di\r\n * aggregazione, dove `Sum` compariva come `Count`.\r\n *\r\n * La direttiva riallinea l'elemento al valore dichiarato a ogni giro di change detection, quindi\r\n * quando le opzioni compaiono la selezione si sistema da se'.\r\n */\r\n\r\nimport { AfterViewChecked, Directive, ElementRef, inject, input } from '@angular/core';\r\n\r\n@Directive({\r\n selector: 'select[dpeValue]',\r\n standalone: true,\r\n})\r\nexport class DpeSelectValueDirective implements AfterViewChecked {\r\n private readonly element = inject<ElementRef<HTMLSelectElement>>(ElementRef);\r\n\r\n readonly dpeValue = input<string | number | null | undefined>('');\r\n\r\n ngAfterViewChecked(): void {\r\n const select = this.element.nativeElement;\r\n const value = this.dpeValue();\r\n const normalized = value === null || value === undefined ? '' : String(value);\r\n if (select.value !== normalized) {\r\n select.value = normalized;\r\n }\r\n }\r\n}\r\n","/**\r\n * Base comune degli ispettori.\r\n *\r\n * `@Directive()` **senza selettore**: e' il solo modo in cui Angular riconosce come input\r\n * quelli dichiarati su una classe base ereditata da piu' componenti. Senza il decoratore ogni\r\n * ispettore darebbe NG8002 sul primo binding.\r\n *\r\n * Ogni ispettore riceve il nodo in **sola lettura** e scrive attraverso lo store: la\r\n * definizione e' un valore, le modifiche producono un nuovo valore. Nessun ispettore muta\r\n * l'oggetto che riceve.\r\n *\r\n * Quello che gli ispettori **non** fanno: non validano. Le sole verifiche locali ammesse sono\r\n * di forma del campo (obbligatorio non vuoto, un intero e' un intero) e servono al\r\n * comportamento del controllo, non a dire se la definizione va bene. Le diagnostiche mostrate\r\n * sono quelle del backend, con il loro codice e percorso.\r\n */\r\n\r\nimport { Directive, computed, inject, input } from '@angular/core';\r\n\r\nimport {\r\n DpeConditionalFields,\r\n dpeDeclares,\r\n dpePruneInapplicable,\r\n} from '../../core/dpe-conditional-fields';\r\nimport { dpeDiagnosticField, dpeDiagnosticTargetsItem } from '../../core/dpe-diagnostics.util';\r\nimport { DpeDocumentStore } from '../../core/dpe-document.store';\r\nimport { dpeFindNode, dpeNodeSourceReferences } from '../../core/dpe-graph';\r\nimport { DpeColumnInfo, DpeExpressionForm } from '../../model/dpe-api.model';\r\nimport { Diagnostic } from '../../model/dpe-diagnostics.model';\r\nimport { DpeAnyNode, DpeNodeCollectionName } from '../../model/dpe-definition.model';\r\n\r\n@Directive()\r\nexport abstract class DpeNodeInspectorBase<TNode extends DpeAnyNode = DpeAnyNode> {\r\n protected readonly store = inject(DpeDocumentStore);\r\n\r\n readonly nodeName = input.required<string>();\r\n readonly node = input.required<TNode>();\r\n\r\n /** La collezione di appartenenza: la sa l'ispettore, serve per scrivere nello store. */\r\n abstract readonly collection: DpeNodeCollectionName;\r\n\r\n /** Le diagnostiche ancorate a questo nodo, come le ha date il backend (**I6**). */\r\n readonly diagnostics = computed<readonly Diagnostic[]>(\r\n () => this.store.diagnosticsByNode().get(this.nodeName())?.diagnostics ?? [],\r\n );\r\n\r\n /**\r\n * Il nodo e' valido ma non compilabile (**I10**): l'ispettore lo dice in testa, perche' e'\r\n * l'informazione che spiega perche' non compare nessun SQL — e non c'e' niente da correggere.\r\n */\r\n readonly isNotCompilable = computed(\r\n () =>\r\n (this.store.diagnosticsByNode().get(this.nodeName())?.isNotCompilable ?? false) ||\r\n this.store.schemaByNode().get(this.nodeName())?.isCompilable === false,\r\n );\r\n\r\n /** Colonne prodotte dal nodo, secondo il backend. Mai calcolate qui (**I4**). */\r\n readonly outputColumns = computed<readonly DpeColumnInfo[]>(\r\n () => this.store.schemaByNode().get(this.nodeName())?.columns ?? [],\r\n );\r\n\r\n /**\r\n * La forma pretesa dai siti di espressione del nodo, **dal backend** (ADR 0031 §1).\r\n *\r\n * Si legge dallo **schema** e non dal catalogo delle formule, ed e' il punto della faccenda: la\r\n * forma e' cio' che la libreria ha derivato su questo nodo — come le colonne e la compilabilita'\r\n * qui sopra — mentre il catalogo e' cio' che il compilatore offre, e **arriva solo quando il\r\n * compilatore risponde**. Finche' viaggiava col catalogo, un ospite che dichiara di non\r\n * descrivere il proprio linguaggio non faceva chiamare la rotta e la forma non arrivava mai.\r\n *\r\n * Assente significa «questo nodo non ospita formule», ed e' uno stato normale: nessuna forma si\r\n * indovina dal tipo di nodo, che sarebbe una regola di dominio in TypeScript.\r\n *\r\n * Sta sulla base e non sui due ispettori che la usano perche' e' un campo dello stesso record da\r\n * cui gia' si leggono `outputColumns` e `isNotCompilable`: leggerlo due volte in due componenti\r\n * significherebbe due letture da tenere allineate.\r\n */\r\n readonly requiredForm = computed<DpeExpressionForm | undefined>(\r\n () => this.store.schemaByNode().get(this.nodeName())?.requiredForm ?? undefined,\r\n );\r\n\r\n /**\r\n * Colonne disponibili **in ingresso**: l'unione degli schemi dei nodi a monte, nell'ordine.\r\n * Arrivano dal backend; se la validazione non e' ancora tornata l'elenco e' vuoto e i campi\r\n * si digitano a mano — che e' meglio di un elenco indovinato in locale.\r\n */\r\n readonly inputColumns = computed<readonly DpeColumnInfo[]>(() => {\r\n const located = dpeFindNode(this.store.definition(), this.nodeName());\r\n if (!located) {\r\n return [];\r\n }\r\n const schemas = this.store.schemaByNode();\r\n const result: DpeColumnInfo[] = [];\r\n const seen = new Set<string>();\r\n for (const reference of dpeNodeSourceReferences(located)) {\r\n if (!reference.isEdge || !reference.sourceName) {\r\n continue;\r\n }\r\n for (const column of schemas.get(reference.sourceName)?.columns ?? []) {\r\n // Confronto insensibile al caso: le colonne si confrontano cosi' in tutto il sistema,\r\n // perche' cosi' si comportano i quattro database di destinazione.\r\n const key = column.name.toLowerCase();\r\n if (!seen.has(key)) {\r\n seen.add(key);\r\n result.push(column);\r\n }\r\n }\r\n }\r\n return result;\r\n });\r\n\r\n /** I parametri dichiarati sulla radice: servono a filtri e mapping dei writeback. */\r\n readonly parameters = computed(() => this.store.definition().parameters ?? []);\r\n\r\n /**\r\n * Le diagnostiche su un campo preciso, per mostrarle **accanto** al controllo.\r\n *\r\n * Il nome del campo si chiede a `dpeDiagnosticField` invece di leggere `path.field` qui:\r\n * `contracts.md` §1 nomina quella funzione come **il** punto in cui l'editor aggancia il rilievo\r\n * al controllo, e una funzione sola che sappia come una diagnostica nomina il proprio campo e'\r\n * la stessa ragione per cui esiste `DiagnosticArgumentKeys`. Il percorso e' gia' pubblicato in\r\n * tre forme diverse: il giorno in cui succede anche al campo, il posto da cambiare e' uno.\r\n */\r\n issuesOn(field: string): Diagnostic[] {\r\n return this.diagnostics().filter((diagnostic) => dpeDiagnosticField(diagnostic) === field);\r\n }\r\n\r\n hasIssueOn(field: string): boolean {\r\n return this.issuesOn(field).length > 0;\r\n }\r\n\r\n /**\r\n * Le diagnostiche su **una riga** di una collezione del nodo (`expressionFields[2].expression`).\r\n *\r\n * Esiste accanto a `issuesOn` perche' quella filtra per solo nome di campo, e in una collezione\r\n * il nome si ripete su ogni riga: l'errore della prima formula finirebbe sotto la terza.\r\n */\r\n issuesOnItem(collection: string, index: number, field: string): Diagnostic[] {\r\n return this.diagnostics().filter((diagnostic) =>\r\n dpeDiagnosticTargetsItem(diagnostic, collection, index, field),\r\n );\r\n }\r\n\r\n /**\r\n * «Il campo porta un valore», nel senso della regola di rendering di `frontend.md` §5-ter:\r\n * **un campo che porta un valore si rende anche fuori dalla propria condizione**.\r\n *\r\n * E' l'unica meta' della regola che serve anche **fuori** dalla tabella delle condizioni\r\n * (`core/dpe-conditional-fields.ts`): un campo dentro una riga di collezione non ha una voce\r\n * in tabella, perche' la ripulitura toglie chiavi del nodo, ma la sua resa segue la stessa\r\n * regola. Il predicato e' quello, non una seconda copia: qui si delega e basta.\r\n */\r\n protected declares(value: unknown): boolean {\r\n return dpeDeclares(value);\r\n }\r\n\r\n /** Applica una modifica al nodo. */\r\n protected patch(patch: Partial<TNode>): void {\r\n this.store.updateNode(this.collection, this.nodeName(), patch as never);\r\n }\r\n\r\n /**\r\n * Cambia un **discriminante** e toglie in un colpo solo i campi che con il valore nuovo non si\r\n * applicano piu', secondo la tabella delle condizioni del nodo.\r\n *\r\n * E' la seconda strada che legge quelle condizioni — la prima e' la resa di §5-ter — e legge la\r\n * **stessa** tabella: una riga aggiunta domani entra qui senza che nessuno la ricopi, che e' il\r\n * punto di `core/dpe-conditional-fields.ts`. La ripulitura resta una modifica come le altre e\r\n * rientra nell'annullamento globale, perche' passa da `patch`.\r\n */\r\n protected patchPruned<TConditions extends DpeConditionalFields<TNode>>(\r\n change: Partial<TNode>,\r\n conditions: TConditions,\r\n ): void {\r\n this.patch(dpePruneInapplicable(this.node(), change, conditions));\r\n }\r\n\r\n /**\r\n * Campo semplice: valore vuoto → chiave **rimossa**, non stringa vuota. Il backend\r\n * distingue assente da vuoto (`DPE_MODEL_REQUIRED_MISSING` contro un valore non valido), e\r\n * su Oracle la stringa vuota **e'** `NULL`: scriverla al posto di un'assenza cambia la\r\n * semantica invece di ripulire il campo.\r\n */\r\n protected setField(field: keyof TNode & string, value: unknown): void {\r\n const patch: Record<string, unknown> = {};\r\n patch[field] = value === '' || value === null ? undefined : value;\r\n this.patch(patch as Partial<TNode>);\r\n }\r\n\r\n /**\r\n * Interruttore il cui **default e' `false`**: spento → chiave **rimossa**, non `false` scritto.\r\n *\r\n * Due conseguenze, e la seconda e' quella che serve a §5-ter. La prima e' **I7**: chi accende e\r\n * rispegne per curiosita' non deve lasciare nel documento una chiave che non c'era. La seconda\r\n * e' che cosi' una casella **sa dire «assente»**: il controllo che rende un campo fuori dalla\r\n * propria condizione deve poterlo riportare all'assenza, e una casella che scrivesse `false`\r\n * resterebbe accesa-o-spenta senza mai togliere niente — resa e inerte, che e' il difetto\r\n * travestito da soluzione.\r\n *\r\n * **Non** si usa dove l'assenza vale `true` (`includesSelfValue`): li' `false` e' una\r\n * dichiarazione vera e toglierla direbbe il contrario.\r\n */\r\n protected setFlag(field: keyof TNode & string, value: boolean): void {\r\n const patch: Record<string, unknown> = {};\r\n patch[field] = value ? true : undefined;\r\n this.patch(patch as Partial<TNode>);\r\n }\r\n\r\n /** Numero da un campo di testo: vuoto o non numerico → assente, non `0`. */\r\n protected toNumber(value: string): number | undefined {\r\n if (value.trim() === '') {\r\n return undefined;\r\n }\r\n const parsed = Number(value);\r\n return Number.isFinite(parsed) ? parsed : undefined;\r\n }\r\n\r\n /** Lettura tipizzata di un valore da un evento di input, senza `any` nei template. */\r\n protected valueOf(event: Event): string {\r\n return (event.target as HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement).value;\r\n }\r\n\r\n protected checkedOf(event: Event): boolean {\r\n return (event.target as HTMLInputElement).checked;\r\n }\r\n}\r\n","/**\r\n * Ispettore dell'aggregazione.\r\n *\r\n * Lo schema di uscita e' `groupBy` (con i loro nomi) **piu'** gli alias delle funzioni, in\r\n * quest'ordine: l'ispettore mostra le due sezioni in quell'ordine per la stessa ragione.\r\n *\r\n * `sourceFieldName` e' ammesso vuoto **solo** per `Count` (conteggio righe). Il contratto lo\r\n * rappresenta con l'assenza e non con la stringa `\"*\"`, quindi il campo si disabilita invece\r\n * di suggerire un valore magico.\r\n */\r\n\r\nimport { ChangeDetectionStrategy, Component, computed } from '@angular/core';\r\n\r\nimport {\r\n AggregateField,\r\n AggregateFunction,\r\n AggregateNode,\r\n DpeNodeCollectionName,\r\n} from '../../model/dpe-definition.model';\r\nimport { DpeSelectValueDirective } from '../shared/dpe-select-value.directive';\r\nimport { DpeNodeInspectorBase } from './dpe-node-inspector-base';\r\n\r\nconst FUNCTIONS: readonly AggregateFunction[] = [\r\n 'Count',\r\n 'CountDistinct',\r\n 'Sum',\r\n 'Min',\r\n 'Max',\r\n 'Avg',\r\n 'StdDev',\r\n 'StdDevPop',\r\n 'Variance',\r\n 'VariancePop',\r\n];\r\n\r\n@Component({\r\n selector: 'dpe-aggregate-inspector',\r\n standalone: true,\r\n imports: [DpeSelectValueDirective],\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n template: `\r\n <div class=\"dpe-form\">\r\n <div class=\"dpe-field\">\r\n <label class=\"dpe-field__label\" for=\"agg-group\">Raggruppa per</label>\r\n <input\r\n id=\"agg-group\"\r\n type=\"text\"\r\n [value]=\"groupByText()\"\r\n placeholder=\"colonne separate da virgola\"\r\n (change)=\"onGroupBy(valueOf($event))\"\r\n />\r\n <p class=\"dpe-field__hint\">\r\n Vuoto = aggregazione globale, una sola riga in uscita.\r\n @if (inputColumns().length > 0) {\r\n Colonne disponibili: {{ columnHint() }}\r\n }\r\n </p>\r\n </div>\r\n\r\n <section class=\"dpe-section\">\r\n <h4 class=\"dpe-section__title\">Funzioni</h4>\r\n @if (fields().length === 0) {\r\n <p class=\"dpe-empty\">Nessuna funzione: il nodo non produce colonne calcolate.</p>\r\n } @else {\r\n <table class=\"dpe-table\">\r\n <thead>\r\n <tr>\r\n <th>Funzione</th>\r\n <th>Colonna</th>\r\n <th>Alias</th>\r\n <th></th>\r\n </tr>\r\n </thead>\r\n <tbody>\r\n @for (field of fields(); track $index) {\r\n <tr>\r\n <td>\r\n <select\r\n [dpeValue]=\"field.function\"\r\n [attr.aria-label]=\"'Funzione ' + ($index + 1)\"\r\n (change)=\"patchField($index, { function: asFunction(valueOf($event)) })\"\r\n >\r\n @for (option of functions; track $index) {\r\n <option [value]=\"option\">{{ option }}</option>\r\n }\r\n </select>\r\n </td>\r\n <td>\r\n <input\r\n type=\"text\"\r\n list=\"agg-columns\"\r\n [value]=\"field.sourceFieldName ?? ''\"\r\n [disabled]=\"field.function === 'Count'\"\r\n [attr.placeholder]=\"field.function === 'Count' ? 'tutte le righe' : ''\"\r\n [attr.aria-label]=\"'Colonna della funzione ' + ($index + 1)\"\r\n (change)=\"patchField($index, { sourceFieldName: valueOf($event) || undefined })\"\r\n />\r\n </td>\r\n <td>\r\n <input\r\n type=\"text\"\r\n [value]=\"field.alias\"\r\n [attr.aria-label]=\"'Alias della funzione ' + ($index + 1)\"\r\n (change)=\"patchField($index, { alias: valueOf($event) })\"\r\n />\r\n </td>\r\n <td>\r\n <button\r\n type=\"button\"\r\n class=\"dpe-btn dpe-btn--icon dpe-btn--danger\"\r\n aria-label=\"Rimuovi la funzione\"\r\n (click)=\"removeField($index)\"\r\n >\r\n ×\r\n </button>\r\n </td>\r\n </tr>\r\n }\r\n </tbody>\r\n </table>\r\n }\r\n <datalist id=\"agg-columns\">\r\n @for (column of inputColumns(); track $index) {\r\n <option [value]=\"column.name\">{{ column.type.kind }}</option>\r\n }\r\n </datalist>\r\n <div>\r\n <button type=\"button\" class=\"dpe-btn\" (click)=\"addField()\">Aggiungi funzione</button>\r\n </div>\r\n <p class=\"dpe-field__hint\">\r\n <code>StdDev</code> e le sue varianti non sono supportate da tutti i dialetti: se il\r\n dialetto scelto non le ha, la validazione lo dice con <code>DPE_DIALECT_UNSUPPORTED</code>.\r\n </p>\r\n </section>\r\n </div>\r\n `,\r\n})\r\nexport class DpeAggregateInspectorComponent extends DpeNodeInspectorBase<AggregateNode> {\r\n override readonly collection: DpeNodeCollectionName = 'aggregates';\r\n\r\n protected readonly functions = FUNCTIONS;\r\n\r\n readonly fields = computed<readonly AggregateField[]>(() => this.node().fields ?? []);\r\n\r\n readonly groupByText = computed(() => (this.node().groupBy ?? []).join(', '));\r\n\r\n readonly columnHint = computed(() => {\r\n const names = this.inputColumns().map((column) => column.name);\r\n return names.length > 10 ? `${names.slice(0, 10).join(', ')}, …` : names.join(', ');\r\n });\r\n\r\n protected asFunction(value: string): AggregateFunction {\r\n return value as AggregateFunction;\r\n }\r\n\r\n protected onGroupBy(raw: string): void {\r\n const columns = raw\r\n .split(',')\r\n .map((value) => value.trim())\r\n .filter((value) => value.length > 0);\r\n this.patch({ groupBy: columns.length > 0 ? columns : undefined });\r\n }\r\n\r\n protected addField(): void {\r\n this.patch({ fields: [...this.fields(), { function: 'Count', alias: '' }] });\r\n }\r\n\r\n protected removeField(index: number): void {\r\n this.patch({ fields: this.fields().filter((_, position) => position !== index) });\r\n }\r\n\r\n protected patchField(index: number, patch: Partial<AggregateField>): void {\r\n this.patch({\r\n fields: this.fields().map((field, position) =>\r\n position === index ? { ...field, ...patch } : field,\r\n ),\r\n });\r\n }\r\n}\r\n","/**\r\n * Ispettore dell'unione.\r\n *\r\n * Le sorgenti si collegano sul canvas: qui si aggiunge o si toglie una **posizione**, cioe' una\r\n * porta. Sono due gesti diversi e vanno in due posti diversi — togliere una porta con l'arco\r\n * attaccato sul canvas non si capirebbe.\r\n *\r\n * `allowsDisjointSchema` cambia il significato dell'unione (schema comune obbligatorio contro\r\n * unione con colonne nulle): l'etichetta lo dice, perche' il nome del campo da solo no.\r\n */\r\n\r\nimport { ChangeDetectionStrategy, Component, computed } from '@angular/core';\r\n\r\nimport { AppendNode, DpeNodeCollectionName } from '../../model/dpe-definition.model';\r\nimport { DpeNodeInspectorBase } from './dpe-node-inspector-base';\r\n\r\n@Component({\r\n selector: 'dpe-append-inspector',\r\n standalone: true,\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n template: `\r\n <div class=\"dpe-form\">\r\n <label class=\"dpe-check\">\r\n <input\r\n type=\"checkbox\"\r\n [checked]=\"node().allowsDisjointSchema ?? false\"\r\n (change)=\"setField('allowsDisjointSchema', checkedOf($event))\"\r\n />\r\n Ammetti schemi diversi\r\n </label>\r\n <p class=\"dpe-field__hint\">\r\n @if (node().allowsDisjointSchema) {\r\n Lo schema in uscita e’ l’unione degli alias: le colonne assenti in una sorgente\r\n diventano nulle.\r\n } @else {\r\n Gli schemi delle sorgenti devono coincidere per nome e tipo compatibile; se non\r\n coincidono, la validazione lo dice con <code>DPE_SCHEMA_INCOMPATIBLE_UNION</code>.\r\n }\r\n </p>\r\n\r\n <section class=\"dpe-section\">\r\n <h4 class=\"dpe-section__title\">Sorgenti ({{ sources().length }})</h4>\r\n <ol class=\"dpe-form\">\r\n @for (source of sources(); track $index) {\r\n <li class=\"dpe-row\">\r\n <span>{{ $index + 1 }}. {{ source || 'non collegata' }}</span>\r\n <button\r\n type=\"button\"\r\n class=\"dpe-btn dpe-btn--icon dpe-btn--danger\"\r\n [attr.aria-label]=\"'Rimuovi la posizione ' + ($index + 1)\"\r\n (click)=\"removeSource($index)\"\r\n >\r\n ×\r\n </button>\r\n </li>\r\n }\r\n </ol>\r\n <div>\r\n <button type=\"button\" class=\"dpe-btn\" (click)=\"addSource()\">Aggiungi posizione</button>\r\n </div>\r\n <p class=\"dpe-field__hint\">Le sorgenti si collegano trascinando un arco sulle porte in alto.</p>\r\n </section>\r\n </div>\r\n `,\r\n})\r\nexport class DpeAppendInspectorComponent extends DpeNodeInspectorBase<AppendNode> {\r\n override readonly collection: DpeNodeCollectionName = 'appends';\r\n\r\n readonly sources = computed<readonly string[]>(() => this.node().sources ?? []);\r\n\r\n protected addSource(): void {\r\n // Stringa vuota e non `undefined`: una posizione vuota **esiste** come porta, e serve un\r\n // elemento nell'array perche' il canvas la disegni.\r\n this.patch({ sources: [...this.sources(), ''] });\r\n }\r\n\r\n protected removeSource(index: number): void {\r\n this.patch({ sources: this.sources().filter((_, position) => position !== index) });\r\n }\r\n}\r\n","/**\r\n * Ispettore del gruppo atomico.\r\n *\r\n * Non e' un nodo del DAG: dichiara che alcune scritture vanno applicate dall'host **nella stessa\r\n * transazione**, nell'ordine dato. La libreria non apre transazioni — lo dice nel risultato e\r\n * l'host obbedisce — e l'avviso serve a non far credere che qui ci sia un `BEGIN`.\r\n *\r\n * I riferimenti sono a `WritebackNode` con destinazione `Database`: l'elenco e' chiuso a quelli,\r\n * perche' un gruppo atomico su un file non significherebbe niente.\r\n */\r\n\r\nimport { ChangeDetectionStrategy, Component, computed } from '@angular/core';\r\n\r\nimport {\r\n AtomicWritebackNode,\r\n AtomicWritebackRelationship,\r\n DpeNodeCollectionName,\r\n} from '../../model/dpe-definition.model';\r\nimport { DpeSelectValueDirective } from '../shared/dpe-select-value.directive';\r\nimport { DpeNodeInspectorBase } from './dpe-node-inspector-base';\r\n\r\n@Component({\r\n selector: 'dpe-atomic-writeback-inspector',\r\n standalone: true,\r\n imports: [DpeSelectValueDirective],\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n template: `\r\n <div class=\"dpe-form\">\r\n <p class=\"dpe-note\">\r\n La libreria non apre transazioni: questo gruppo <strong>dichiara</strong> che le scritture\r\n elencate vanno applicate insieme, e l’host lo esegue.\r\n </p>\r\n\r\n <div class=\"dpe-field\">\r\n <label class=\"dpe-field__label\" for=\"aw-sequence\">Sequenza</label>\r\n <input\r\n id=\"aw-sequence\"\r\n type=\"number\"\r\n min=\"1\"\r\n [value]=\"node().sequence\"\r\n [attr.aria-invalid]=\"hasIssueOn('sequence')\"\r\n (change)=\"setField('sequence', toNumber(valueOf($event)) ?? 1)\"\r\n />\r\n <p class=\"dpe-field__hint\">Nello stesso spazio di numerazione delle scritture.</p>\r\n </div>\r\n\r\n <section class=\"dpe-section\">\r\n <h4 class=\"dpe-section__title\">Relazioni</h4>\r\n @if (relationships().length === 0) {\r\n <p class=\"dpe-empty\">Nessuna relazione.</p>\r\n }\r\n @for (relationship of relationships(); track $index) {\r\n <div class=\"dpe-section\">\r\n <div class=\"dpe-row\">\r\n <div class=\"dpe-field\">\r\n <label class=\"dpe-field__label\" [attr.for]=\"'aw-parent-' + $index\">Scrittura padre</label>\r\n <select\r\n [id]=\"'aw-parent-' + $index\"\r\n [dpeValue]=\"relationship.parentWritebackName ?? ''\"\r\n (change)=\"patchRelationship($index, { parentWritebackName: valueOf($event) || undefined })\"\r\n >\r\n <option value=\"\">—</option>\r\n @for (writeback of databaseWritebacks(); track $index) {\r\n <option [value]=\"writeback.name\">{{ writeback.name }}</option>\r\n }\r\n </select>\r\n </div>\r\n <div class=\"dpe-field\">\r\n <label class=\"dpe-field__label\" [attr.for]=\"'aw-child-' + $index\">Scrittura figlia</label>\r\n <select\r\n [id]=\"'aw-child-' + $index\"\r\n [dpeValue]=\"relationship.childWritebackName ?? ''\"\r\n (change)=\"patchRelationship($index, { childWritebackName: valueOf($event) || undefined })\"\r\n >\r\n <option value=\"\">—</option>\r\n @for (writeback of databaseWritebacks(); track $index) {\r\n <option [value]=\"writeback.name\">{{ writeback.name }}</option>\r\n }\r\n </select>\r\n </div>\r\n <div class=\"dpe-field\">\r\n <label class=\"dpe-field__label\" [attr.for]=\"'aw-seq-' + $index\">Ordine</label>\r\n <input\r\n [id]=\"'aw-seq-' + $index\"\r\n type=\"number\"\r\n min=\"1\"\r\n [value]=\"relationship.sequence\"\r\n (change)=\"patchRelationship($index, { sequence: toNumber(valueOf($event)) ?? 1 })\"\r\n />\r\n </div>\r\n </div>\r\n <div class=\"dpe-row\">\r\n <div class=\"dpe-field\">\r\n <label class=\"dpe-field__label\" [attr.for]=\"'aw-pfield-' + $index\">Campo padre</label>\r\n <input\r\n [id]=\"'aw-pfield-' + $index\"\r\n type=\"text\"\r\n [value]=\"relationship.parentFieldName ?? ''\"\r\n (change)=\"patchRelationship($index, { parentFieldName: valueOf($event) || undefined })\"\r\n />\r\n </div>\r\n <div class=\"dpe-field\">\r\n <label class=\"dpe-field__label\" [attr.for]=\"'aw-cfield-' + $index\">Campo figlio</label>\r\n <input\r\n [id]=\"'aw-cfield-' + $index\"\r\n type=\"text\"\r\n [value]=\"relationship.childFieldName ?? ''\"\r\n (change)=\"patchRelationship($index, { childFieldName: valueOf($event) || undefined })\"\r\n />\r\n </div>\r\n <div class=\"dpe-field\">\r\n <label class=\"dpe-field__label\" [attr.for]=\"'aw-rel-' + $index\">Relazione</label>\r\n <input\r\n [id]=\"'aw-rel-' + $index\"\r\n type=\"text\"\r\n [value]=\"relationship.relationshipName ?? ''\"\r\n (change)=\"patchRelationship($index, { relationshipName: valueOf($event) || undefined })\"\r\n />\r\n </div>\r\n <button\r\n type=\"button\"\r\n class=\"dpe-btn dpe-btn--danger\"\r\n [attr.aria-label]=\"'Rimuovi la relazione ' + ($index + 1)\"\r\n (click)=\"removeRelationship($index)\"\r\n >\r\n Rimuovi\r\n </button>\r\n </div>\r\n </div>\r\n }\r\n <div>\r\n <button type=\"button\" class=\"dpe-btn\" (click)=\"addRelationship()\">Aggiungi relazione</button>\r\n </div>\r\n </section>\r\n </div>\r\n `,\r\n})\r\nexport class DpeAtomicWritebackInspectorComponent extends DpeNodeInspectorBase<AtomicWritebackNode> {\r\n override readonly collection: DpeNodeCollectionName = 'atomicWritebacks';\r\n\r\n readonly relationships = computed<readonly AtomicWritebackRelationship[]>(\r\n () => this.node().relationships ?? [],\r\n );\r\n\r\n readonly databaseWritebacks = computed(() =>\r\n (this.store.definition().writebacks ?? []).filter((writeback) => writeback.target === 'Database'),\r\n );\r\n\r\n protected addRelationship(): void {\r\n const nextSequence =\r\n this.relationships().reduce((max, relationship) => Math.max(max, relationship.sequence), 0) + 1;\r\n this.patch({ relationships: [...this.relationships(), { sequence: nextSequence }] });\r\n }\r\n\r\n protected removeRelationship(index: number): void {\r\n this.patch({\r\n relationships: this.relationships().filter((_, position) => position !== index),\r\n });\r\n }\r\n\r\n protected patchRelationship(index: number, patch: Partial<AtomicWritebackRelationship>): void {\r\n this.patch({\r\n relationships: this.relationships().map((relationship, position) =>\r\n position === index ? { ...relationship, ...patch } : relationship,\r\n ),\r\n });\r\n }\r\n}\r\n","/**\r\n * Editor di un `ColumnType`: i sei tipi logici e i loro attributi condizionali.\r\n *\r\n * I campi che compaiono dipendono dal `kind`, e la dipendenza e' quella del contratto:\r\n * `length` per `String`, `precision`/`scale` per `Number`, `allowedValues` per `Enum`. Mostrare\r\n * sempre tutto renderebbe rappresentabile un tipo che il backend rifiuta; mostrare solo cio'\r\n * che serve rende quello stato non rappresentabile — **tranne quando lo stato c'e' gia'**\r\n * (`frontend.md` §5-ter): un attributo che porta un valore si rende anche sul tipo che non lo\r\n * prevede, altrimenti `onKind` lo azzera solo per chi passa di qui e un documento scritto\r\n * altrove se lo tiene per sempre.\r\n *\r\n * Cosa **non** fa: non decide se `length` sia obbligatorio in questo punto della definizione —\r\n * lo e' nei campi *dichiarati* e non altrove, e a dirlo e' la validazione\r\n * (`DPE_MODEL_CONDITIONAL_REQUIRED`).\r\n */\r\n\r\nimport { ChangeDetectionStrategy, Component, computed, input, output } from '@angular/core';\r\n\r\nimport { ColumnType, LogicalType } from '../../model/dpe-definition.model';\r\n\r\nconst LOGICAL_TYPES: readonly LogicalType[] = [\r\n 'String',\r\n 'Number',\r\n 'Integer',\r\n 'DateTime',\r\n 'Boolean',\r\n 'Enum',\r\n];\r\n\r\nimport { DpeSelectValueDirective } from './dpe-select-value.directive';\r\n\r\n@Component({\r\n selector: 'dpe-column-type-editor',\r\n standalone: true,\r\n imports: [DpeSelectValueDirective],\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n template: `\r\n <div class=\"dpe-row\">\r\n <div class=\"dpe-field\">\r\n <label class=\"dpe-field__label\" [attr.for]=\"controlId() + '-kind'\">Tipo</label>\r\n <select [id]=\"controlId() + '-kind'\" [dpeValue]=\"kind()\" (change)=\"onKind(text($event))\">\r\n @for (option of types; track $index) {\r\n <option [value]=\"option\">{{ option }}</option>\r\n }\r\n </select>\r\n </div>\r\n\r\n <!--\r\n Gli attributi si rendono per il proprio tipo **oppure quando portano un valore**\r\n (frontend.md §5-ter): una lunghezza arrivata su un DateTime da un documento scritto\r\n altrove deve restare visibile e svuotabile, che qui vuol dire cancellare il numero. Con\r\n l’attributo nascosto resterebbe nel documento per sempre, e nessuno lo segnala.\r\n -->\r\n @if (showsLength()) {\r\n <div class=\"dpe-field\">\r\n <label class=\"dpe-field__label\" [attr.for]=\"controlId() + '-length'\">Lunghezza</label>\r\n <input\r\n type=\"number\"\r\n min=\"1\"\r\n [id]=\"controlId() + '-length'\"\r\n [value]=\"value()?.length ?? ''\"\r\n (change)=\"onNumber('length', text($event))\"\r\n />\r\n </div>\r\n }\r\n\r\n @if (showsPrecision()) {\r\n <div class=\"dpe-field\">\r\n <label class=\"dpe-field__label\" [attr.for]=\"controlId() + '-precision'\">Precisione</label>\r\n <input\r\n type=\"number\"\r\n min=\"1\"\r\n [id]=\"controlId() + '-precision'\"\r\n [value]=\"value()?.precision ?? ''\"\r\n (change)=\"onNumber('precision', text($event))\"\r\n />\r\n </div>\r\n <div class=\"dpe-field\">\r\n <label class=\"dpe-field__label\" [attr.for]=\"controlId() + '-scale'\">Decimali</label>\r\n <input\r\n type=\"number\"\r\n min=\"0\"\r\n [id]=\"controlId() + '-scale'\"\r\n [value]=\"value()?.scale ?? ''\"\r\n (change)=\"onNumber('scale', text($event))\"\r\n />\r\n </div>\r\n }\r\n </div>\r\n\r\n @if (showsAllowedValues()) {\r\n <div class=\"dpe-field\">\r\n <label class=\"dpe-field__label\" [attr.for]=\"controlId() + '-values'\">Valori ammessi</label>\r\n <input\r\n type=\"text\"\r\n [id]=\"controlId() + '-values'\"\r\n [value]=\"allowedValues()\"\r\n placeholder=\"separati da virgola\"\r\n (change)=\"onAllowedValues(text($event))\"\r\n />\r\n </div>\r\n }\r\n `,\r\n})\r\nexport class DpeColumnTypeEditorComponent {\r\n readonly value = input<ColumnType | undefined>(undefined);\r\n readonly controlId = input('dpe-type');\r\n readonly valueChange = output<ColumnType>();\r\n\r\n protected readonly types = LOGICAL_TYPES;\r\n\r\n readonly kind = computed<LogicalType>(() => this.value()?.kind ?? 'String');\r\n\r\n readonly allowedValues = computed(() => (this.value()?.allowedValues ?? []).join(', '));\r\n\r\n /*\r\n * Gli attributi condizionali del tipo, nella forma di `frontend.md` §5-ter: **il tipo li\r\n * prevede oppure il valore c'e'**. Questo componente non estende `DpeNodeInspectorBase` — non\r\n * e' un ispettore, e' un controllo — quindi la condizione e' scritta qui, ed e' banale perche'\r\n * sono tre numeri e una lista: assente vale assente e basta.\r\n */\r\n readonly showsLength = computed(\r\n () => this.kind() === 'String' || this.value()?.length !== undefined,\r\n );\r\n\r\n readonly showsPrecision = computed(\r\n () =>\r\n this.kind() === 'Number' ||\r\n this.value()?.precision !== undefined ||\r\n this.value()?.scale !== undefined,\r\n );\r\n\r\n readonly showsAllowedValues = computed(\r\n () => this.kind() === 'Enum' || (this.value()?.allowedValues?.length ?? 0) > 0,\r\n );\r\n\r\n protected onKind(kind: string): void {\r\n // Cambiare tipo **azzera** gli attributi dell'altro: una `length` rimasta su un `Number`\r\n // sarebbe un campo che il backend ignora oggi e rifiuta domani.\r\n this.valueChange.emit({ kind: kind as LogicalType });\r\n }\r\n\r\n protected onNumber(field: 'length' | 'precision' | 'scale', raw: string): void {\r\n const parsed = raw.trim() === '' ? undefined : Number(raw);\r\n this.valueChange.emit({\r\n ...(this.value() ?? { kind: this.kind() }),\r\n [field]: Number.isFinite(parsed) ? parsed : undefined,\r\n });\r\n }\r\n\r\n protected onAllowedValues(raw: string): void {\r\n const values = raw\r\n .split(',')\r\n .map((value) => value.trim())\r\n .filter((value) => value.length > 0);\r\n this.valueChange.emit({\r\n ...(this.value() ?? { kind: 'Enum' }),\r\n allowedValues: values.length > 0 ? values : undefined,\r\n });\r\n }\r\n\r\n protected text(event: Event): string {\r\n return (event.target as HTMLInputElement | HTMLSelectElement).value;\r\n }\r\n}\r\n","/**\r\n * Ispettore del nodo personalizzato.\r\n *\r\n * `outputFields` non e' un campo come gli altri: **e' l'unica cosa che tiene in piedi lo schema\r\n * a valle** (**I4**). Senza uno schema dichiarato la propagazione si interrompe e tutto cio' che\r\n * sta dopo diventa invalidabile — quindi l'avviso non e' generico, dice esattamente questo.\r\n *\r\n * Come il `Forecast`, e' modellato e non compilabile in v1.\r\n */\r\n\r\nimport { ChangeDetectionStrategy, Component, computed } from '@angular/core';\r\n\r\nimport {\r\n ColumnType,\r\n CustomNode,\r\n CustomNodeParameter,\r\n DeclaredField,\r\n DpeNodeCollectionName,\r\n} from '../../model/dpe-definition.model';\r\nimport { DpeColumnTypeEditorComponent } from '../shared/dpe-column-type-editor.component';\r\nimport { DpeNodeInspectorBase } from './dpe-node-inspector-base';\r\n\r\n@Component({\r\n selector: 'dpe-custom-node-inspector',\r\n standalone: true,\r\n imports: [DpeColumnTypeEditorComponent],\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n template: `\r\n <div class=\"dpe-form\">\r\n <p class=\"dpe-note dpe-note--not-compilable\">\r\n Nodo modellato per intero ma <strong>non traducibile in SQL</strong> nella versione 1.\r\n </p>\r\n\r\n <div class=\"dpe-row\">\r\n <div class=\"dpe-field\">\r\n <label class=\"dpe-field__label\" for=\"cn-extension\">Estensione</label>\r\n <input\r\n id=\"cn-extension\"\r\n type=\"text\"\r\n [value]=\"node().extensionName ?? ''\"\r\n (change)=\"setField('extensionName', valueOf($event))\"\r\n />\r\n </div>\r\n <div class=\"dpe-field\">\r\n <label class=\"dpe-field__label\" for=\"cn-namespace\">Namespace</label>\r\n <input\r\n id=\"cn-namespace\"\r\n type=\"text\"\r\n [value]=\"node().extensionNamespace ?? ''\"\r\n (change)=\"setField('extensionNamespace', valueOf($event))\"\r\n />\r\n </div>\r\n </div>\r\n\r\n <section class=\"dpe-section\">\r\n <h4 class=\"dpe-section__title\">Sorgenti ({{ sources().length }})</h4>\r\n @for (source of sources(); track $index) {\r\n <div class=\"dpe-row\">\r\n <span>{{ $index + 1 }}. {{ source || 'non collegata' }}</span>\r\n <button\r\n type=\"button\"\r\n class=\"dpe-btn dpe-btn--icon dpe-btn--danger\"\r\n [attr.aria-label]=\"'Rimuovi la posizione ' + ($index + 1)\"\r\n (click)=\"removeSource($index)\"\r\n >\r\n ×\r\n </button>\r\n </div>\r\n }\r\n <div>\r\n <button type=\"button\" class=\"dpe-btn\" (click)=\"addSource()\">Aggiungi posizione</button>\r\n </div>\r\n </section>\r\n\r\n <section class=\"dpe-section\">\r\n <h4 class=\"dpe-section__title\">Parametri dell’estensione</h4>\r\n @for (parameter of nodeParameters(); track $index) {\r\n <div class=\"dpe-row\">\r\n <input\r\n type=\"text\"\r\n [value]=\"parameter.name\"\r\n [attr.aria-label]=\"'Nome del parametro ' + ($index + 1)\"\r\n (change)=\"patchParameter($index, { name: valueOf($event) })\"\r\n />\r\n <input\r\n type=\"text\"\r\n [value]=\"parameter.value\"\r\n [attr.aria-label]=\"'Valore del parametro ' + ($index + 1)\"\r\n (change)=\"patchParameter($index, { value: valueOf($event) })\"\r\n />\r\n <button\r\n type=\"button\"\r\n class=\"dpe-btn dpe-btn--icon dpe-btn--danger\"\r\n aria-label=\"Rimuovi il parametro\"\r\n (click)=\"removeParameter($index)\"\r\n >\r\n ×\r\n </button>\r\n </div>\r\n }\r\n <div>\r\n <button type=\"button\" class=\"dpe-btn\" (click)=\"addParameter()\">Aggiungi parametro</button>\r\n </div>\r\n </section>\r\n\r\n <section class=\"dpe-section\">\r\n <h4 class=\"dpe-section__title\">Schema in uscita</h4>\r\n <p class=\"dpe-note dpe-note--warning\">\r\n Va dichiarato qui: non esiste un registro delle estensioni da cui ricavarlo. Se manca,\r\n la propagazione dello schema si interrompe e <strong>tutto cio’ che sta a valle</strong>\r\n diventa invalidabile.\r\n </p>\r\n @for (field of outputFields(); track $index) {\r\n <div class=\"dpe-section\">\r\n <div class=\"dpe-row\">\r\n <div class=\"dpe-field\">\r\n <label class=\"dpe-field__label\" [attr.for]=\"'cn-field-' + $index\">Nome</label>\r\n <input\r\n [id]=\"'cn-field-' + $index\"\r\n type=\"text\"\r\n [value]=\"field.name\"\r\n (change)=\"patchOutput($index, { name: valueOf($event) })\"\r\n />\r\n </div>\r\n <button\r\n type=\"button\"\r\n class=\"dpe-btn dpe-btn--danger\"\r\n [attr.aria-label]=\"'Rimuovi la colonna ' + field.name\"\r\n (click)=\"removeOutput($index)\"\r\n >\r\n Rimuovi\r\n </button>\r\n </div>\r\n <dpe-column-type-editor\r\n [value]=\"field.type\"\r\n [controlId]=\"'cn-type-' + $index\"\r\n (valueChange)=\"patchOutput($index, { type: $event })\"\r\n />\r\n </div>\r\n }\r\n <div>\r\n <button type=\"button\" class=\"dpe-btn\" (click)=\"addOutput()\">Aggiungi colonna</button>\r\n </div>\r\n </section>\r\n </div>\r\n `,\r\n})\r\nexport class DpeCustomNodeInspectorComponent extends DpeNodeInspectorBase<CustomNode> {\r\n override readonly collection: DpeNodeCollectionName = 'customNodes';\r\n\r\n readonly sources = computed<readonly string[]>(() => this.node().sources ?? []);\r\n readonly nodeParameters = computed<readonly CustomNodeParameter[]>(\r\n () => this.node().parameters ?? [],\r\n );\r\n readonly outputFields = computed<readonly DeclaredField[]>(() => this.node().outputFields ?? []);\r\n\r\n protected addSource(): void {\r\n this.patch({ sources: [...this.sources(), ''] });\r\n }\r\n\r\n protected removeSource(index: number): void {\r\n this.patch({ sources: this.sources().filter((_, position) => position !== index) });\r\n }\r\n\r\n protected addParameter(): void {\r\n this.patch({ parameters: [...this.nodeParameters(), { name: '', value: '' }] });\r\n }\r\n\r\n protected removeParameter(index: number): void {\r\n this.patch({\r\n parameters: this.nodeParameters().filter((_, position) => position !== index),\r\n });\r\n }\r\n\r\n protected patchParameter(index: number, patch: Partial<CustomNodeParameter>): void {\r\n this.patch({\r\n parameters: this.nodeParameters().map((parameter, position) =>\r\n position === index ? { ...parameter, ...patch } : parameter,\r\n ),\r\n });\r\n }\r\n\r\n protected addOutput(): void {\r\n this.patch({\r\n outputFields: [...this.outputFields(), { name: '', type: { kind: 'String' } }],\r\n });\r\n }\r\n\r\n protected removeOutput(index: number): void {\r\n this.patch({ outputFields: this.outputFields().filter((_, position) => position !== index) });\r\n }\r\n\r\n protected patchOutput(index: number, patch: Partial<DeclaredField> & { type?: ColumnType }): void {\r\n this.patch({\r\n outputFields: this.outputFields().map((field, position) =>\r\n position === index ? { ...field, ...patch } : field,\r\n ),\r\n });\r\n }\r\n}\r\n","/**\r\n * Ispettore della sorgente.\r\n *\r\n * I campi che compaiono dipendono dal `kind`: `tableName` per `Table`, le opzioni CSV per\r\n * `Csv`, il nome del dataset per `ExternalDataset`. Il `declaredType` di un campo si mostra\r\n * **solo** dove serve — per `Table` il tipo lo da' il provider di metadati e quello dichiarato\r\n * viene ignorato: mostrarlo farebbe credere di poterlo decidere.\r\n *\r\n * «Dipendono dal `kind`» con l'eccezione normativa di `frontend.md` §5-ter, che vale per tutti e\r\n * quattro: **un campo che porta un valore si rende anche fuori dalla propria condizione**, e con\r\n * un controllo capace di riportarlo all'assenza. Nessuno dei quattro e' diagnosticato fuori\r\n * condizione, il che li rende il caso peggiore e non quello trascurabile: un valore che nessuno\r\n * segnala, che nessuna schermata mostra, e che il primo cambio di `kind` rimette in gioco.\r\n *\r\n * I tre esiti del provider (ADR 0004) restano distinti: tabella dichiarata, dichiarata **senza\r\n * campi**, sconosciuta. Nel secondo caso i nomi dei campi si digitano a mano e non c'e' niente\r\n * da correggere; nel terzo la diagnostica autorevole e' `DPE_REF_UNKNOWN_TABLE` del backend.\r\n */\r\n\r\nimport { ChangeDetectionStrategy, Component, computed, effect, inject } from '@angular/core';\r\n\r\nimport {\r\n DPE_DATA_SOURCE_CONDITIONS,\r\n dpeFieldVisibility,\r\n} from '../../core/dpe-conditional-fields';\r\nimport { DpeMetadataStore } from '../../core/dpe-metadata.store';\r\nimport {\r\n ColumnType,\r\n CsvDelimiter,\r\n DataSourceField,\r\n DataSourceKind,\r\n DataSourceNode,\r\n DpeNodeCollectionName,\r\n} from '../../model/dpe-definition.model';\r\nimport { DpeColumnTypeEditorComponent } from '../shared/dpe-column-type-editor.component';\r\nimport { DpeSelectValueDirective } from '../shared/dpe-select-value.directive';\r\nimport { DpeNodeInspectorBase } from './dpe-node-inspector-base';\r\n\r\nconst KINDS: readonly DataSourceKind[] = ['Table', 'Csv', 'ExternalDataset'];\r\nconst DELIMITERS: readonly CsvDelimiter[] = ['Comma', 'Semicolon', 'Tab', 'Pipe', 'Caret', 'Backquote'];\r\n\r\n@Component({\r\n selector: 'dpe-data-source-inspector',\r\n standalone: true,\r\n imports: [DpeColumnTypeEditorComponent, DpeSelectValueDirective],\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n template: `\r\n <div class=\"dpe-form\">\r\n <div class=\"dpe-field\">\r\n <label class=\"dpe-field__label\" for=\"ds-kind\">Tipo di sorgente</label>\r\n <select id=\"ds-kind\" [dpeValue]=\"node().kind\" (change)=\"setField('kind', valueOf($event))\">\r\n @for (kind of kinds; track $index) {\r\n <option [value]=\"kind\">{{ kind }}</option>\r\n }\r\n </select>\r\n </div>\r\n\r\n <!--\r\n Tre blocchi indipendenti e non uno switch: la regola di rendering di frontend.md §5-ter\r\n e’ normativa e vale qui come sull’ispettore della scrittura — **un campo che porta un\r\n valore si rende anche fuori dalla propria condizione**. Con uno switch il nome di tabella\r\n di una sorgente diventata Csv non lo vedrebbe piu’ nessuno e non lo potrebbe togliere\r\n nessuno, e qui il backend **non dice niente**: fuori da Table quel valore non e’ vietato,\r\n e’ ignorato. Vuoto e non applicabile resta nascosto, cosi’ il caso normale non guadagna\r\n rumore.\r\n -->\r\n @if (shows.tableName()) {\r\n <div class=\"dpe-field\">\r\n <label class=\"dpe-field__label\" for=\"ds-table\">Tabella</label>\r\n <input\r\n id=\"ds-table\"\r\n type=\"text\"\r\n list=\"ds-table-options\"\r\n [value]=\"node().tableName ?? ''\"\r\n [attr.aria-invalid]=\"hasIssueOn('tableName')\"\r\n (change)=\"setField('tableName', valueOf($event))\"\r\n />\r\n <!--\r\n Elenco proposto (datalist) e non tendina chiusa: le tabelle possono essere molte e\r\n il nome resta digitabile — un catalogo incompleto non deve impedire di scrivere.\r\n -->\r\n <datalist id=\"ds-table-options\">\r\n @for (table of tables(); track $index) {\r\n <option [value]=\"table.name\">{{ table.description || table.name }}</option>\r\n }\r\n </datalist>\r\n <p class=\"dpe-field__hint\">{{ tableHint() }}</p>\r\n @for (issue of issuesOn('tableName'); track $index) {\r\n <p class=\"dpe-field__issue\">{{ issue.code }} — {{ issue.message }}</p>\r\n }\r\n </div>\r\n }\r\n\r\n @if (shows.csvOptions()) {\r\n <div class=\"dpe-field\">\r\n <label class=\"dpe-field__label\" for=\"ds-location\">Posizione del file</label>\r\n <input\r\n id=\"ds-location\"\r\n type=\"text\"\r\n [value]=\"node().csvOptions?.location ?? ''\"\r\n (change)=\"patchCsv({ location: valueOf($event) })\"\r\n />\r\n <p class=\"dpe-field__hint\">Identificatore opaco: lo risolve l’host, non la libreria.</p>\r\n </div>\r\n <div class=\"dpe-row\">\r\n <div class=\"dpe-field\">\r\n <label class=\"dpe-field__label\" for=\"ds-delimiter\">Separatore</label>\r\n <select\r\n id=\"ds-delimiter\"\r\n [dpeValue]=\"node().csvOptions?.delimiter ?? 'Comma'\"\r\n (change)=\"patchCsv({ delimiter: valueOf($event) })\"\r\n >\r\n @for (delimiter of delimiters; track $index) {\r\n <option [value]=\"delimiter\">{{ delimiter }}</option>\r\n }\r\n </select>\r\n </div>\r\n <label class=\"dpe-check\">\r\n <input\r\n type=\"checkbox\"\r\n [checked]=\"node().csvOptions?.hasHeaderRow ?? true\"\r\n (change)=\"patchCsv({ hasHeaderRow: checkedOf($event) })\"\r\n />\r\n Prima riga di intestazione\r\n </label>\r\n <!--\r\n Il comando c’e’ quando **l’oggetto c’e’**, dentro e fuori dalla propria condizione:\r\n una tendina e una casella sanno proporre un altro valore, non sanno dire «assente»,\r\n e senza questo comando le opzioni rimaste su una sorgente diventata Table sarebbero\r\n rese e inerti — cioe’ visibili e ancora non cancellabili.\r\n -->\r\n @if (node().csvOptions) {\r\n <button type=\"button\" class=\"dpe-btn\" (click)=\"removeCsvOptions()\">\r\n Rimuovi le opzioni CSV\r\n </button>\r\n }\r\n </div>\r\n }\r\n\r\n @if (shows.externalDatasetName()) {\r\n <div class=\"dpe-field\">\r\n <label class=\"dpe-field__label\" for=\"ds-dataset\">Dataset esterno</label>\r\n <input\r\n id=\"ds-dataset\"\r\n type=\"text\"\r\n [value]=\"node().externalDatasetName ?? ''\"\r\n (change)=\"setField('externalDatasetName', valueOf($event))\"\r\n />\r\n </div>\r\n }\r\n\r\n <section class=\"dpe-section\">\r\n <h4 class=\"dpe-section__title\">Campi</h4>\r\n @if (fields().length === 0) {\r\n <p class=\"dpe-empty\">Nessun campo: la sorgente non produce colonne.</p>\r\n } @else {\r\n <table class=\"dpe-table\">\r\n <thead>\r\n <tr>\r\n <th>Nome</th>\r\n <th>Alias</th>\r\n @if (showsTypes()) {\r\n <th>Tipo</th>\r\n }\r\n <th>PK</th>\r\n <th></th>\r\n </tr>\r\n </thead>\r\n <tbody>\r\n @for (field of fields(); track $index) {\r\n <tr>\r\n <td>\r\n <input\r\n type=\"text\"\r\n list=\"ds-field-options\"\r\n [value]=\"field.name\"\r\n [attr.aria-label]=\"'Nome del campo ' + ($index + 1)\"\r\n (change)=\"patchField($index, { name: valueOf($event) })\"\r\n />\r\n </td>\r\n <td>\r\n <input\r\n type=\"text\"\r\n [value]=\"field.alias ?? ''\"\r\n [attr.aria-label]=\"'Alias del campo ' + ($index + 1)\"\r\n (change)=\"patchField($index, { alias: valueOf($event) || undefined })\"\r\n />\r\n </td>\r\n @if (showsTypes()) {\r\n <td>\r\n <dpe-column-type-editor\r\n [value]=\"field.declaredType\"\r\n [controlId]=\"'ds-field-' + $index\"\r\n (valueChange)=\"patchField($index, { declaredType: $event })\"\r\n />\r\n <!--\r\n L’editor di tipo emette **sempre** un tipo: non sa dire «nessuno». Su una\r\n sorgente Table il tipo dichiarato il backend lo ignora, quindi qui serve\r\n il comando che lo toglie, altrimenti la colonna comparirebbe senza poter\r\n tornare all’assenza. C’e’ quando il tipo c’e’, dentro e fuori condizione.\r\n -->\r\n @if (field.declaredType) {\r\n <button\r\n type=\"button\"\r\n class=\"dpe-btn\"\r\n [attr.aria-label]=\"'Rimuovi il tipo dichiarato del campo ' + ($index + 1)\"\r\n (click)=\"patchField($index, { declaredType: undefined })\"\r\n >\r\n Rimuovi il tipo\r\n </button>\r\n }\r\n </td>\r\n }\r\n <td>\r\n <input\r\n type=\"checkbox\"\r\n [checked]=\"field.isPrimaryKey ?? false\"\r\n [attr.aria-label]=\"'Chiave primaria del campo ' + ($index + 1)\"\r\n (change)=\"patchField($index, { isPrimaryKey: checkedOf($event) })\"\r\n />\r\n </td>\r\n <td>\r\n <button\r\n type=\"button\"\r\n class=\"dpe-btn dpe-btn--icon dpe-btn--danger\"\r\n [attr.aria-label]=\"'Rimuovi il campo ' + field.name\"\r\n (click)=\"removeField($index)\"\r\n >\r\n ×\r\n </button>\r\n </td>\r\n </tr>\r\n }\r\n </tbody>\r\n </table>\r\n }\r\n <datalist id=\"ds-field-options\">\r\n @for (candidate of providerFields(); track $index) {\r\n <option [value]=\"candidate.name\">{{ candidate.type.kind }}</option>\r\n }\r\n </datalist>\r\n <div>\r\n <button type=\"button\" class=\"dpe-btn\" (click)=\"addField()\">Aggiungi campo</button>\r\n </div>\r\n </section>\r\n </div>\r\n `,\r\n})\r\nexport class DpeDataSourceInspectorComponent extends DpeNodeInspectorBase<DataSourceNode> {\r\n private readonly metadata = inject(DpeMetadataStore);\r\n\r\n override readonly collection: DpeNodeCollectionName = 'dataSources';\r\n\r\n protected readonly kinds = KINDS;\r\n protected readonly delimiters = DELIMITERS;\r\n\r\n readonly tables = computed(() => this.metadata.tables());\r\n\r\n readonly fields = computed<readonly DataSourceField[]>(() => this.node().fields ?? []);\r\n\r\n /*\r\n * I tre campi condizionali **del nodo**, uno per voce di `DPE_DATA_SOURCE_CONDITIONS`, tutti\r\n * con la stessa forma: **la condizione vale oppure il campo porta un valore** (`frontend.md`\r\n * §5-ter). Nessuno dei tre e' diagnosticato fuori condizione — il backend ignora e basta — e\r\n * non cambia niente: la regola guarda il documento, non l'elenco dei codici.\r\n */\r\n readonly shows = dpeFieldVisibility(this.node, DPE_DATA_SOURCE_CONDITIONS);\r\n\r\n /**\r\n * Il tipo si dichiara solo dove non c'e' un catalogo da interrogare — **oppure** dove qualche\r\n * campo ne porta gia' uno: su una `Table` il tipo dichiarato il backend lo ignora, e una\r\n * colonna che nessuno mostra e' un valore che nessuno puo' togliere.\r\n *\r\n * Questa **non** e' una voce della tabella delle condizioni, ed e' l'unica del suo genere: li'\r\n * ogni riga e' una chiave del nodo, perche' la ripulitura toglie chiavi del nodo. Qui il campo\r\n * governato sta **dentro una riga di `fields`**, e la meta' «porta un valore» e' un `some(...)`\r\n * sulle righe invece della lettura di una chiave. La forma della regola resta quella, e il\r\n * predicato e' lo stesso (`declares`).\r\n */\r\n readonly showsTypes = computed(\r\n () =>\r\n this.node().kind !== 'Table' ||\r\n this.fields().some((field) => this.declares(field.declaredType)),\r\n );\r\n\r\n readonly providerFields = computed(() => {\r\n const tableName = this.node().tableName;\r\n if (!tableName) {\r\n return [];\r\n }\r\n return this.metadata.stateOf(tableName)?.table?.fields ?? [];\r\n });\r\n\r\n /** I tre esiti in una frase: sono situazioni diverse e vanno dette diverse. */\r\n readonly tableHint = computed(() => {\r\n const tableName = this.node().tableName;\r\n if (!tableName) {\r\n return 'Nome noto al provider di metadati dell’host, non al catalogo del database.';\r\n }\r\n const state = this.metadata.stateOf(tableName);\r\n if (!state) {\r\n return 'Lettura dei metadati in corso…';\r\n }\r\n switch (state.outcome) {\r\n case 'Declared':\r\n return `Tabella dichiarata: ${state.table?.fields?.length ?? 0} campi noti.`;\r\n case 'DeclaredWithoutFields':\r\n return 'Tabella dichiarata senza campi: i nomi si scrivono a mano, non c’e’ nulla da correggere.';\r\n case 'Unknown':\r\n return 'Tabella sconosciuta al provider: la validazione lo segnalera’.';\r\n default:\r\n return '';\r\n }\r\n });\r\n\r\n constructor() {\r\n super();\r\n this.metadata.loadTables();\r\n // Si chiede la descrizione della tabella riferita, una volta per nome. La cache e' dello\r\n // store: cambiare nodo e tornare indietro non rifa' la chiamata.\r\n effect(() => {\r\n const tableName = this.node().tableName;\r\n if (tableName) {\r\n this.metadata.describe(tableName);\r\n }\r\n });\r\n }\r\n\r\n protected addField(): void {\r\n this.patch({ fields: [...this.fields(), { name: '' }] });\r\n }\r\n\r\n protected removeField(index: number): void {\r\n this.patch({ fields: this.fields().filter((_, position) => position !== index) });\r\n }\r\n\r\n protected patchField(\r\n index: number,\r\n patch: Partial<DataSourceField> & { declaredType?: ColumnType },\r\n ): void {\r\n this.patch({\r\n fields: this.fields().map((field, position) =>\r\n position === index ? { ...field, ...patch } : field,\r\n ),\r\n });\r\n }\r\n\r\n protected patchCsv(patch: Partial<{ location: string; delimiter: string; hasHeaderRow: boolean }>): void {\r\n const current = this.node().csvOptions ?? { location: '' };\r\n this.patch({\r\n csvOptions: { ...current, ...(patch as Partial<typeof current>) },\r\n });\r\n }\r\n\r\n /*\r\n * Si toglie l'oggetto intero, non se ne svuotano i campi: un `csvOptions: {}` resta un\r\n * `csvOptions` dichiarato, e su una sorgente che non e' `Csv` e' esattamente il valore che\r\n * questa regola esiste per rendere togliibile.\r\n */\r\n protected removeCsvOptions(): void {\r\n this.patch({ csvOptions: undefined });\r\n }\r\n}\r\n","/**\r\n * Ispettore del filtro.\r\n *\r\n * Il vincolo condizionale del contratto governa cosa si vede: con `isDynamic` falso servono i\r\n * criteri e la `condition`; con `isDynamic` vero serve il nome del parametro di filtro, e i\r\n * criteri non si mostrano — **a meno che il documento non ne porti**, e allora si mostrano\r\n * (`frontend.md` §5-ter, normativa). Non e' un'attenuazione del vincolo: renderli non li rende\r\n * leciti, e a dire che non lo sono resta il backend con `DPE_MODEL_CONDITIONAL_FORBIDDEN`. E'\r\n * che un valore senza controllo non e' togliibile, e per **I7** sopravvive a ogni salvataggio.\r\n *\r\n * `condition` non e' SQL: e' una combinazione booleana dei numeri di sequenza dei criteri\r\n * (`1 AND (2 OR 3)`), ed e' l'unico linguaggio che il backend parsifica davvero. L'etichetta lo\r\n * dice, perche' una casella accanto a \"SQL nativo\" altrove invita a scriverci dentro SQL.\r\n */\r\n\r\nimport { ChangeDetectionStrategy, Component, computed } from '@angular/core';\r\n\r\nimport {\r\n DPE_FILTER_CONDITIONS,\r\n dpeFieldVisibility,\r\n} from '../../core/dpe-conditional-fields';\r\nimport {\r\n DpeNodeCollectionName,\r\n FilterCriterion,\r\n FilterNode,\r\n FilterOperator,\r\n} from '../../model/dpe-definition.model';\r\nimport { DpeSelectValueDirective } from '../shared/dpe-select-value.directive';\r\nimport { DpeNodeInspectorBase } from './dpe-node-inspector-base';\r\n\r\nconst OPERATORS: readonly FilterOperator[] = [\r\n 'Equals',\r\n 'NotEquals',\r\n 'GreaterThan',\r\n 'GreaterThanOrEqual',\r\n 'LessThan',\r\n 'LessThanOrEqual',\r\n 'StartsWith',\r\n 'EndsWith',\r\n 'Contains',\r\n 'DoesNotContain',\r\n 'IsNull',\r\n 'IsNotNull',\r\n 'In',\r\n 'NotIn',\r\n];\r\n\r\n/** Gli unici operatori che non prendono un valore: e' forma del controllo, non validazione. */\r\nconst UNARY: readonly FilterOperator[] = ['IsNull', 'IsNotNull'];\r\n\r\n@Component({\r\n selector: 'dpe-filter-inspector',\r\n standalone: true,\r\n imports: [DpeSelectValueDirective],\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n template: `\r\n <div class=\"dpe-form\">\r\n <label class=\"dpe-check\">\r\n <input\r\n type=\"checkbox\"\r\n [checked]=\"node().isDynamic ?? false\"\r\n (change)=\"setField('isDynamic', checkedOf($event))\"\r\n />\r\n Filtro dinamico (i criteri arrivano da un parametro)\r\n </label>\r\n\r\n <!--\r\n Le due meta’ del filtro non sono piu’ esclusive: per frontend.md §5-ter, normativa, **un\r\n campo che porta un valore si rende anche fuori dalla propria condizione**. Un documento\r\n scritto altrove puo’ portare i criteri **e** il parametro insieme, e con un ramo\r\n alternativo una delle due meta’ resterebbe invisibile e non cancellabile — il rilievo del\r\n backend (DPE_MODEL_CONDITIONAL_FORBIDDEN) arriverebbe su un campo senza controllo. Vuoto\r\n e non applicabile resta nascosto: nel caso normale si vede una meta’ sola, come prima.\r\n -->\r\n @if (shows.filterParameterName()) {\r\n <div class=\"dpe-field\">\r\n <label class=\"dpe-field__label\" for=\"filter-parameter\">Parametro di filtro</label>\r\n <select\r\n id=\"filter-parameter\"\r\n [dpeValue]=\"node().filterParameterName ?? ''\"\r\n (change)=\"setField('filterParameterName', valueOf($event))\"\r\n >\r\n <option value=\"\">— non impostato —</option>\r\n @for (parameter of filterParameters(); track $index) {\r\n <option [value]=\"parameter.name\">{{ parameter.label }} ({{ parameter.name }})</option>\r\n }\r\n </select>\r\n @if (filterParameters().length === 0) {\r\n <p class=\"dpe-field__hint\">\r\n Nessun parametro con ruolo <code>Filter</code> dichiarato sulla definizione.\r\n </p>\r\n }\r\n </div>\r\n }\r\n\r\n @if (shows.criteria()) {\r\n <section class=\"dpe-section\">\r\n <h4 class=\"dpe-section__title\">Criteri</h4>\r\n @if (criteria().length === 0) {\r\n <p class=\"dpe-empty\">Nessun criterio.</p>\r\n } @else {\r\n <table class=\"dpe-table\">\r\n <thead>\r\n <tr>\r\n <th>Seq.</th>\r\n <th>Colonna</th>\r\n <th>Operatore</th>\r\n <th>Valore</th>\r\n <th>Parametro</th>\r\n <th></th>\r\n </tr>\r\n </thead>\r\n <tbody>\r\n @for (criterion of criteria(); track $index) {\r\n <tr>\r\n <td>\r\n <input\r\n type=\"number\"\r\n min=\"1\"\r\n [value]=\"criterion.sequence\"\r\n [attr.aria-label]=\"'Sequenza del criterio ' + ($index + 1)\"\r\n (change)=\"patchCriterion($index, { sequence: number(valueOf($event)) })\"\r\n />\r\n </td>\r\n <td>\r\n <input\r\n type=\"text\"\r\n list=\"filter-columns\"\r\n [value]=\"criterion.sourceFieldName\"\r\n [attr.aria-label]=\"'Colonna del criterio ' + ($index + 1)\"\r\n (change)=\"patchCriterion($index, { sourceFieldName: valueOf($event) })\"\r\n />\r\n </td>\r\n <td>\r\n <select\r\n [dpeValue]=\"criterion.operator\"\r\n [attr.aria-label]=\"'Operatore del criterio ' + ($index + 1)\"\r\n (change)=\"patchCriterion($index, { operator: operator(valueOf($event)) })\"\r\n >\r\n @for (option of operators; track $index) {\r\n <option [value]=\"option\">{{ option }}</option>\r\n }\r\n </select>\r\n </td>\r\n <td>\r\n @if (!isUnary(criterion.operator)) {\r\n <input\r\n type=\"text\"\r\n [value]=\"criterion.value ?? ''\"\r\n [disabled]=\"!!criterion.inputVariable\"\r\n [attr.aria-label]=\"'Valore del criterio ' + ($index + 1)\"\r\n (change)=\"patchCriterion($index, { value: valueOf($event) || undefined })\"\r\n />\r\n }\r\n </td>\r\n <td>\r\n @if (!isUnary(criterion.operator)) {\r\n <!--\r\n Valore e parametro sono mutuamente esclusivi: sceglierne uno disabilita\r\n l’altro, cosi' lo stato \"entrambi\" non e’ rappresentabile.\r\n -->\r\n <select\r\n [dpeValue]=\"criterion.inputVariable ?? ''\"\r\n [attr.aria-label]=\"'Parametro del criterio ' + ($index + 1)\"\r\n (change)=\"onInputVariable($index, valueOf($event))\"\r\n >\r\n <option value=\"\">— nessuno —</option>\r\n @for (parameter of valueParameters(); track $index) {\r\n <option [value]=\"parameter.name\">{{ parameter.name }}</option>\r\n }\r\n </select>\r\n }\r\n </td>\r\n <td>\r\n <button\r\n type=\"button\"\r\n class=\"dpe-btn dpe-btn--icon dpe-btn--danger\"\r\n [attr.aria-label]=\"'Rimuovi il criterio ' + criterion.sequence\"\r\n (click)=\"removeCriterion($index)\"\r\n >\r\n ×\r\n </button>\r\n </td>\r\n </tr>\r\n }\r\n </tbody>\r\n </table>\r\n }\r\n <datalist id=\"filter-columns\">\r\n @for (column of inputColumns(); track $index) {\r\n <option [value]=\"column.name\">{{ column.type.kind }}</option>\r\n }\r\n </datalist>\r\n <div>\r\n <button type=\"button\" class=\"dpe-btn\" (click)=\"addCriterion()\">Aggiungi criterio</button>\r\n </div>\r\n </section>\r\n }\r\n\r\n @if (shows.condition()) {\r\n <div class=\"dpe-field\">\r\n <label class=\"dpe-field__label\" for=\"filter-condition\">Condizione sui numeri di sequenza</label>\r\n <input\r\n id=\"filter-condition\"\r\n type=\"text\"\r\n [value]=\"node().condition ?? ''\"\r\n placeholder=\"1 AND (2 OR 3)\"\r\n [attr.aria-invalid]=\"hasIssueOn('condition')\"\r\n (change)=\"setField('condition', valueOf($event))\"\r\n />\r\n <p class=\"dpe-field__hint\">\r\n Non e’ SQL: solo numeri di sequenza, <code>AND</code>, <code>OR</code>,\r\n <code>NOT</code> e parentesi.\r\n </p>\r\n @for (issue of issuesOn('condition'); track $index) {\r\n <p class=\"dpe-field__issue\">{{ issue.code }} — {{ issue.message }}</p>\r\n }\r\n </div>\r\n }\r\n </div>\r\n `,\r\n})\r\nexport class DpeFilterInspectorComponent extends DpeNodeInspectorBase<FilterNode> {\r\n override readonly collection: DpeNodeCollectionName = 'filters';\r\n\r\n protected readonly operators = OPERATORS;\r\n\r\n readonly criteria = computed<readonly FilterCriterion[]>(() => this.node().criteria ?? []);\r\n\r\n /*\r\n * I tre campi condizionali del filtro, uno per voce di `DPE_FILTER_CONDITIONS` e stessa forma:\r\n * **la condizione vale oppure il campo porta un valore** (`frontend.md` §5-ter). Un criterio si\r\n * toglie con il proprio ×, la condizione svuotando il testo, il parametro con la voce «— non\r\n * impostato —»: ognuno dei tre controlli sa gia' riportare il campo all'assenza, che e' la meta'\r\n * della regola senza la quale renderli non servirebbe a niente.\r\n */\r\n readonly shows = dpeFieldVisibility(this.node, DPE_FILTER_CONDITIONS);\r\n\r\n readonly filterParameters = computed(() =>\r\n this.parameters().filter((parameter) => parameter.role === 'Filter'),\r\n );\r\n\r\n readonly valueParameters = computed(() =>\r\n this.parameters().filter((parameter) => parameter.role === 'Value'),\r\n );\r\n\r\n protected isUnary(operator: FilterOperator): boolean {\r\n return UNARY.includes(operator);\r\n }\r\n\r\n protected operator(value: string): FilterOperator {\r\n return value as FilterOperator;\r\n }\r\n\r\n protected number(value: string): number {\r\n // Forma del campo: un numero di sequenza non numerico non e' rappresentabile, e 1 e' il\r\n // minimo dichiarato. Non e' validazione: l'unicita' la verifica il backend.\r\n const parsed = Number(value);\r\n return Number.isFinite(parsed) && parsed >= 1 ? Math.trunc(parsed) : 1;\r\n }\r\n\r\n protected addCriterion(): void {\r\n const nextSequence =\r\n this.criteria().reduce((max, criterion) => Math.max(max, criterion.sequence), 0) + 1;\r\n this.patch({\r\n criteria: [\r\n ...this.criteria(),\r\n { sequence: nextSequence, sourceFieldName: '', operator: 'Equals' },\r\n ],\r\n });\r\n }\r\n\r\n protected removeCriterion(index: number): void {\r\n this.patch({ criteria: this.criteria().filter((_, position) => position !== index) });\r\n }\r\n\r\n protected patchCriterion(index: number, patch: Partial<FilterCriterion>): void {\r\n this.patch({\r\n criteria: this.criteria().map((criterion, position) =>\r\n position === index ? { ...criterion, ...patch } : criterion,\r\n ),\r\n });\r\n }\r\n\r\n /** Scegliere un parametro **toglie** il valore letterale: sono esclusivi per contratto. */\r\n protected onInputVariable(index: number, name: string): void {\r\n this.patchCriterion(index, {\r\n inputVariable: name === '' ? undefined : name,\r\n value: name === '' ? this.criteria()[index]?.value : undefined,\r\n });\r\n }\r\n}\r\n","/**\r\n * Ispettore della previsione.\r\n *\r\n * Il nodo e' **modellato per intero e non compilabile in v1**: l'avviso sta in testa e non a\r\n * pie' di pagina, perche' e' la prima cosa da sapere prima di costruirci una pipeline intorno.\r\n * Non e' un errore dell'utente — e' `DPE_NODE_NOT_COMPILABLE`, l'unico codice che significa\r\n * \"hai fatto bene, siamo noi\" — quindi il colore non e' quello dell'errore.\r\n */\r\n\r\nimport { ChangeDetectionStrategy, Component, computed } from '@angular/core';\r\n\r\nimport {\r\n AggregateFunction,\r\n DatePart,\r\n DpeNodeCollectionName,\r\n ForecastAccuracy,\r\n ForecastAggregationField,\r\n ForecastGroupField,\r\n ForecastModelType,\r\n ForecastNode,\r\n ForecastPeriodType,\r\n} from '../../model/dpe-definition.model';\r\nimport { DpeSelectValueDirective } from '../shared/dpe-select-value.directive';\r\nimport { DpeNodeInspectorBase } from './dpe-node-inspector-base';\r\n\r\nconst PERIOD_TYPES: readonly ForecastPeriodType[] = [\r\n 'Year',\r\n 'YearQuarter',\r\n 'YearMonth',\r\n 'YearWeek',\r\n 'YearMonthDay',\r\n 'FiscalYear',\r\n 'FiscalYearQuarter',\r\n 'FiscalYearMonth',\r\n 'FiscalYearWeek',\r\n];\r\n\r\nconst MODELS: readonly ForecastModelType[] = ['Auto', 'Additive', 'Multiplicative'];\r\nconst ACCURACIES: readonly ForecastAccuracy[] = ['None', 'Eighty', 'NinetyFive'];\r\nconst FUNCTIONS: readonly AggregateFunction[] = ['Sum', 'Avg', 'Min', 'Max', 'Count'];\r\nconst DATE_PARTS: readonly DatePart[] = [\r\n 'Second',\r\n 'Minute',\r\n 'Hour',\r\n 'Day',\r\n 'Week',\r\n 'Month',\r\n 'Quarter',\r\n 'Year',\r\n];\r\n\r\n@Component({\r\n selector: 'dpe-forecast-inspector',\r\n standalone: true,\r\n imports: [DpeSelectValueDirective],\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n template: `\r\n <div class=\"dpe-form\">\r\n <p class=\"dpe-note dpe-note--not-compilable\">\r\n Nodo modellato per intero ma <strong>non traducibile in SQL</strong> nella versione 1:\r\n puoi descriverlo e salvarlo, la compilazione lo rifiuta con\r\n <code>DPE_NODE_NOT_COMPILABLE</code>. Non c’e’ niente da correggere.\r\n </p>\r\n\r\n <div class=\"dpe-row\">\r\n <div class=\"dpe-field\">\r\n <label class=\"dpe-field__label\" for=\"fc-date\">Campo data</label>\r\n <input\r\n id=\"fc-date\"\r\n type=\"text\"\r\n list=\"fc-columns\"\r\n [value]=\"node().dateFieldName ?? ''\"\r\n (change)=\"setField('dateFieldName', valueOf($event))\"\r\n />\r\n </div>\r\n <div class=\"dpe-field\">\r\n <label class=\"dpe-field__label\" for=\"fc-period\">Periodo</label>\r\n <select\r\n id=\"fc-period\"\r\n [dpeValue]=\"node().periodType ?? 'YearMonth'\"\r\n (change)=\"setField('periodType', valueOf($event))\"\r\n >\r\n @for (period of periodTypes; track $index) {\r\n <option [value]=\"period\">{{ period }}</option>\r\n }\r\n </select>\r\n </div>\r\n </div>\r\n\r\n <div class=\"dpe-row\">\r\n <div class=\"dpe-field\">\r\n <label class=\"dpe-field__label\" for=\"fc-count\">Numero di periodi</label>\r\n <input\r\n id=\"fc-count\"\r\n type=\"number\"\r\n min=\"1\"\r\n max=\"100\"\r\n [value]=\"node().periodCount ?? 1\"\r\n (change)=\"setField('periodCount', toNumber(valueOf($event)))\"\r\n />\r\n </div>\r\n <div class=\"dpe-field\">\r\n <label class=\"dpe-field__label\" for=\"fc-start\">Alias inizio periodo</label>\r\n <input\r\n id=\"fc-start\"\r\n type=\"text\"\r\n [value]=\"node().periodStartFieldName ?? ''\"\r\n (change)=\"setField('periodStartFieldName', valueOf($event))\"\r\n />\r\n </div>\r\n </div>\r\n\r\n <div class=\"dpe-row\">\r\n <div class=\"dpe-field\">\r\n <label class=\"dpe-field__label\" for=\"fc-model\">Modello</label>\r\n <select\r\n id=\"fc-model\"\r\n [dpeValue]=\"node().modelType ?? 'Auto'\"\r\n (change)=\"setField('modelType', valueOf($event))\"\r\n >\r\n @for (model of models; track $index) {\r\n <option [value]=\"model\">{{ model }}</option>\r\n }\r\n </select>\r\n </div>\r\n <div class=\"dpe-field\">\r\n <label class=\"dpe-field__label\" for=\"fc-seasonality\">Stagionalita’</label>\r\n <input\r\n id=\"fc-seasonality\"\r\n type=\"number\"\r\n min=\"2\"\r\n max=\"24\"\r\n [value]=\"node().seasonality ?? ''\"\r\n placeholder=\"automatica\"\r\n (change)=\"setField('seasonality', toNumber(valueOf($event)))\"\r\n />\r\n </div>\r\n <div class=\"dpe-field\">\r\n <label class=\"dpe-field__label\" for=\"fc-accuracy\">Intervallo di confidenza</label>\r\n <select\r\n id=\"fc-accuracy\"\r\n [dpeValue]=\"node().accuracyPercent ?? 'None'\"\r\n (change)=\"setField('accuracyPercent', valueOf($event))\"\r\n >\r\n @for (accuracy of accuracies; track $index) {\r\n <option [value]=\"accuracy\">{{ accuracy }}</option>\r\n }\r\n </select>\r\n </div>\r\n </div>\r\n\r\n <label class=\"dpe-check\">\r\n <input\r\n type=\"checkbox\"\r\n [checked]=\"node().excludesLastPeriod ?? false\"\r\n (change)=\"setField('excludesLastPeriod', checkedOf($event))\"\r\n />\r\n Escludi l’ultimo periodo (spesso incompleto)\r\n </label>\r\n\r\n <section class=\"dpe-section\">\r\n <h4 class=\"dpe-section__title\">Campi da prevedere</h4>\r\n @for (field of aggregationFields(); track $index) {\r\n <div class=\"dpe-row\">\r\n <input\r\n type=\"text\"\r\n list=\"fc-columns\"\r\n [value]=\"field.fieldName\"\r\n [attr.aria-label]=\"'Campo ' + ($index + 1)\"\r\n (change)=\"patchAggregation($index, { fieldName: valueOf($event) })\"\r\n />\r\n <select\r\n [dpeValue]=\"field.function\"\r\n [attr.aria-label]=\"'Funzione ' + ($index + 1)\"\r\n (change)=\"patchAggregation($index, { function: asFunction(valueOf($event)) })\"\r\n >\r\n @for (option of functions; track $index) {\r\n <option [value]=\"option\">{{ option }}</option>\r\n }\r\n </select>\r\n <input\r\n type=\"text\"\r\n [value]=\"field.resultAlias\"\r\n [attr.aria-label]=\"'Alias ' + ($index + 1)\"\r\n (change)=\"patchAggregation($index, { resultAlias: valueOf($event) })\"\r\n />\r\n <button\r\n type=\"button\"\r\n class=\"dpe-btn dpe-btn--icon dpe-btn--danger\"\r\n aria-label=\"Rimuovi il campo\"\r\n (click)=\"removeAggregation($index)\"\r\n >\r\n ×\r\n </button>\r\n </div>\r\n }\r\n <div>\r\n <button type=\"button\" class=\"dpe-btn\" (click)=\"addAggregation()\">Aggiungi campo</button>\r\n </div>\r\n </section>\r\n\r\n <section class=\"dpe-section\">\r\n <h4 class=\"dpe-section__title\">Raggruppamenti</h4>\r\n @for (group of groupFields(); track $index) {\r\n <div class=\"dpe-row\">\r\n <input\r\n type=\"text\"\r\n list=\"fc-columns\"\r\n [value]=\"group.fieldName\"\r\n [attr.aria-label]=\"'Campo di raggruppamento ' + ($index + 1)\"\r\n (change)=\"patchGroup($index, { fieldName: valueOf($event) })\"\r\n />\r\n <select\r\n [dpeValue]=\"group.datePart ?? ''\"\r\n [attr.aria-label]=\"'Parte di data ' + ($index + 1)\"\r\n (change)=\"patchGroup($index, { datePart: asDatePart(valueOf($event)) })\"\r\n >\r\n <option value=\"\">— nessuna —</option>\r\n @for (part of dateParts; track $index) {\r\n <option [value]=\"part\">{{ part }}</option>\r\n }\r\n </select>\r\n <button\r\n type=\"button\"\r\n class=\"dpe-btn dpe-btn--icon dpe-btn--danger\"\r\n aria-label=\"Rimuovi il raggruppamento\"\r\n (click)=\"removeGroup($index)\"\r\n >\r\n ×\r\n </button>\r\n </div>\r\n }\r\n <p class=\"dpe-field__hint\">\r\n La parte di data e’ richiesta quando il campo e’ di tipo <code>DateTime</code>.\r\n </p>\r\n <div>\r\n <button type=\"button\" class=\"dpe-btn\" (click)=\"addGroup()\">Aggiungi raggruppamento</button>\r\n </div>\r\n </section>\r\n\r\n <datalist id=\"fc-columns\">\r\n @for (column of inputColumns(); track $index) {\r\n <option [value]=\"column.name\">{{ column.type.kind }}</option>\r\n }\r\n </datalist>\r\n </div>\r\n `,\r\n})\r\nexport class DpeForecastInspectorComponent extends DpeNodeInspectorBase<ForecastNode> {\r\n override readonly collection: DpeNodeCollectionName = 'forecasts';\r\n\r\n protected readonly periodTypes = PERIOD_TYPES;\r\n protected readonly models = MODELS;\r\n protected readonly accuracies = ACCURACIES;\r\n protected readonly functions = FUNCTIONS;\r\n protected readonly dateParts = DATE_PARTS;\r\n\r\n readonly aggregationFields = computed<readonly ForecastAggregationField[]>(\r\n () => this.node().aggregationFields ?? [],\r\n );\r\n\r\n readonly groupFields = computed<readonly ForecastGroupField[]>(() => this.node().groupFields ?? []);\r\n\r\n protected asFunction(value: string): AggregateFunction {\r\n return value as AggregateFunction;\r\n }\r\n\r\n protected asDatePart(value: string): DatePart | undefined {\r\n return value === '' ? undefined : (value as DatePart);\r\n }\r\n\r\n protected addAggregation(): void {\r\n this.patch({\r\n aggregationFields: [\r\n ...this.aggregationFields(),\r\n { fieldName: '', function: 'Sum', resultAlias: '' },\r\n ],\r\n });\r\n }\r\n\r\n protected removeAggregation(index: number): void {\r\n this.patch({\r\n aggregationFields: this.aggregationFields().filter((_, position) => position !== index),\r\n });\r\n }\r\n\r\n protected patchAggregation(index: number, patch: Partial<ForecastAggregationField>): void {\r\n this.patch({\r\n aggregationFields: this.aggregationFields().map((field, position) =>\r\n position === index ? { ...field, ...patch } : field,\r\n ),\r\n });\r\n }\r\n\r\n protected addGroup(): void {\r\n this.patch({ groupFields: [...this.groupFields(), { fieldName: '' }] });\r\n }\r\n\r\n protected removeGroup(index: number): void {\r\n this.patch({ groupFields: this.groupFields().filter((_, position) => position !== index) });\r\n }\r\n\r\n protected patchGroup(index: number, patch: Partial<ForecastGroupField>): void {\r\n this.patch({\r\n groupFields: this.groupFields().map((group, position) =>\r\n position === index ? { ...group, ...patch } : group,\r\n ),\r\n });\r\n }\r\n}\r\n","/**\r\n * Ispettore del percorso gerarchico.\r\n *\r\n * E' il nodo che pone il vincolo di capacita' sul dialetto: richiede CTE ricorsive. Non lo\r\n * verifichiamo qui — `DPE_DIALECT_UNSUPPORTED` arriva dalla validazione — ma lo diciamo, perche'\r\n * un utente che non sa che questo nodo dipende dal dialetto non capirebbe la diagnostica.\r\n *\r\n * I campi di aggregazione compaiono solo con `isAggregationRequired`: e' il vincolo condizionale\r\n * del contratto, e senza il flag sono campi che il backend rifiuta.\r\n */\r\n\r\nimport { ChangeDetectionStrategy, Component, computed } from '@angular/core';\r\n\r\nimport {\r\n DPE_HIERARCHY_PATH_CONDITIONS,\r\n dpeFieldVisibility,\r\n} from '../../core/dpe-conditional-fields';\r\nimport {\r\n AggregateField,\r\n AggregateFunction,\r\n DpeNodeCollectionName,\r\n HierarchyPathNode,\r\n} from '../../model/dpe-definition.model';\r\nimport { DpeNodeInspectorBase } from './dpe-node-inspector-base';\r\n\r\nconst FUNCTIONS: readonly AggregateFunction[] = [\r\n 'Count',\r\n 'CountDistinct',\r\n 'Sum',\r\n 'Min',\r\n 'Max',\r\n 'Avg',\r\n];\r\n\r\n@Component({\r\n selector: 'dpe-hierarchy-path-inspector',\r\n standalone: true,\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n template: `\r\n <div class=\"dpe-form\">\r\n <p class=\"dpe-note\">\r\n Richiede le CTE ricorsive del dialetto di destinazione: se il dialetto scelto non le\r\n supporta, la validazione lo segnala come limite del <strong>dialetto</strong>, non della\r\n definizione.\r\n </p>\r\n\r\n <div class=\"dpe-row\">\r\n <div class=\"dpe-field\">\r\n <label class=\"dpe-field__label\" for=\"hp-self\">Campo chiave</label>\r\n <input\r\n id=\"hp-self\"\r\n type=\"text\"\r\n list=\"hp-columns\"\r\n [value]=\"node().selfFieldName ?? ''\"\r\n (change)=\"setField('selfFieldName', valueOf($event))\"\r\n />\r\n </div>\r\n <div class=\"dpe-field\">\r\n <label class=\"dpe-field__label\" for=\"hp-parent\">Campo padre</label>\r\n <input\r\n id=\"hp-parent\"\r\n type=\"text\"\r\n list=\"hp-columns\"\r\n [value]=\"node().parentFieldName ?? ''\"\r\n (change)=\"setField('parentFieldName', valueOf($event))\"\r\n />\r\n </div>\r\n </div>\r\n\r\n <div class=\"dpe-field\">\r\n <label class=\"dpe-field__label\" for=\"hp-path\">Alias della colonna di percorso</label>\r\n <input\r\n id=\"hp-path\"\r\n type=\"text\"\r\n [value]=\"node().pathFieldName ?? ''\"\r\n (change)=\"setField('pathFieldName', valueOf($event))\"\r\n />\r\n <p class=\"dpe-field__hint\">La produce il backend: e’ di tipo <code>String</code> e non nulla.</p>\r\n </div>\r\n\r\n <label class=\"dpe-check\">\r\n <input\r\n type=\"checkbox\"\r\n [checked]=\"node().includesSelfValue ?? true\"\r\n (change)=\"setField('includesSelfValue', checkedOf($event))\"\r\n />\r\n Includi il valore del nodo corrente nel percorso\r\n </label>\r\n\r\n <div class=\"dpe-field\">\r\n <label class=\"dpe-field__label\" for=\"hp-rollup\">Alias della percentuale di rollup</label>\r\n <input\r\n id=\"hp-rollup\"\r\n type=\"text\"\r\n [value]=\"node().rollupPercentageFieldName ?? ''\"\r\n (change)=\"setField('rollupPercentageFieldName', valueOf($event))\"\r\n />\r\n </div>\r\n\r\n <label class=\"dpe-check\">\r\n <input\r\n type=\"checkbox\"\r\n [checked]=\"node().isAggregationRequired ?? false\"\r\n (change)=\"setField('isAggregationRequired', checkedOf($event))\"\r\n />\r\n Aggrega lungo la gerarchia\r\n </label>\r\n\r\n <!--\r\n frontend.md §5-ter: i campi aggregati si rendono anche con l’aggregazione spenta, quando\r\n ce ne sono. Senza, chi ne dichiara e poi toglie la spunta si porta dietro una collezione\r\n che non vede piu’ nessuno e che nessuno puo’ svuotare — e per **I7** sopravvive a ogni\r\n salvataggio. Collezione vuota vale assente: spenta e senza campi, la sezione non compare.\r\n -->\r\n @if (shows.aggregateFields()) {\r\n <section class=\"dpe-section\">\r\n <h4 class=\"dpe-section__title\">Campi aggregati</h4>\r\n @if (fields().length === 0) {\r\n <p class=\"dpe-empty\">Nessun campo: con l’aggregazione attiva ne serve almeno uno.</p>\r\n } @else {\r\n <table class=\"dpe-table\">\r\n <thead>\r\n <tr>\r\n <th>Funzione</th>\r\n <th>Colonna</th>\r\n <th>Alias</th>\r\n <th></th>\r\n </tr>\r\n </thead>\r\n <tbody>\r\n @for (field of fields(); track $index) {\r\n <tr>\r\n <td>\r\n <select\r\n [value]=\"field.function\"\r\n [attr.aria-label]=\"'Funzione ' + ($index + 1)\"\r\n (change)=\"patchField($index, { function: asFunction(valueOf($event)) })\"\r\n >\r\n @for (option of functions; track $index) {\r\n <option [value]=\"option\">{{ option }}</option>\r\n }\r\n </select>\r\n </td>\r\n <td>\r\n <input\r\n type=\"text\"\r\n list=\"hp-columns\"\r\n [value]=\"field.sourceFieldName ?? ''\"\r\n [disabled]=\"field.function === 'Count'\"\r\n [attr.aria-label]=\"'Colonna ' + ($index + 1)\"\r\n (change)=\"patchField($index, { sourceFieldName: valueOf($event) || undefined })\"\r\n />\r\n </td>\r\n <td>\r\n <input\r\n type=\"text\"\r\n [value]=\"field.alias\"\r\n [attr.aria-label]=\"'Alias ' + ($index + 1)\"\r\n (change)=\"patchField($index, { alias: valueOf($event) })\"\r\n />\r\n </td>\r\n <td>\r\n <button\r\n type=\"button\"\r\n class=\"dpe-btn dpe-btn--icon dpe-btn--danger\"\r\n aria-label=\"Rimuovi il campo\"\r\n (click)=\"removeField($index)\"\r\n >\r\n ×\r\n </button>\r\n </td>\r\n </tr>\r\n }\r\n </tbody>\r\n </table>\r\n }\r\n <div>\r\n <button type=\"button\" class=\"dpe-btn\" (click)=\"addField()\">Aggiungi campo</button>\r\n </div>\r\n </section>\r\n }\r\n\r\n <datalist id=\"hp-columns\">\r\n @for (column of inputColumns(); track $index) {\r\n <option [value]=\"column.name\">{{ column.type.kind }}</option>\r\n }\r\n </datalist>\r\n </div>\r\n `,\r\n})\r\nexport class DpeHierarchyPathInspectorComponent extends DpeNodeInspectorBase<HierarchyPathNode> {\r\n override readonly collection: DpeNodeCollectionName = 'hierarchyPaths';\r\n\r\n protected readonly functions = FUNCTIONS;\r\n\r\n readonly fields = computed<readonly AggregateField[]>(() => this.node().aggregateFields ?? []);\r\n\r\n /** L'unico campo condizionale di qui, nella forma di `frontend.md` §5-ter. */\r\n readonly shows = dpeFieldVisibility(this.node, DPE_HIERARCHY_PATH_CONDITIONS);\r\n\r\n protected asFunction(value: string): AggregateFunction {\r\n return value as AggregateFunction;\r\n }\r\n\r\n protected addField(): void {\r\n this.patch({ aggregateFields: [...this.fields(), { function: 'Sum', alias: '' }] });\r\n }\r\n\r\n protected removeField(index: number): void {\r\n this.patch({ aggregateFields: this.fields().filter((_, position) => position !== index) });\r\n }\r\n\r\n protected patchField(index: number, patch: Partial<AggregateField>): void {\r\n this.patch({\r\n aggregateFields: this.fields().map((field, position) =>\r\n position === index ? { ...field, ...patch } : field,\r\n ),\r\n });\r\n }\r\n}\r\n","/**\r\n * Ispettore del join.\r\n *\r\n * Due cose che il canvas non puo' dire e che quindi stanno qui: quale sorgente e' la\r\n * **primaria** (il lato che sopravvive in un `LeftOuter`) e lo **schema di uscita esplicito** —\r\n * un join produce solo i campi dichiarati in `fields`, non l'unione dei due lati.\r\n *\r\n * `allowsMultipleLookupValues` compare solo con `kind === 'Lookup'`: altrove il contratto lo\r\n * dichiara non significativo, e un interruttore che non fa niente e' peggio di un interruttore\r\n * assente.\r\n */\r\n\r\nimport { ChangeDetectionStrategy, Component, computed } from '@angular/core';\r\n\r\nimport {\r\n DPE_JOIN_CONDITIONS,\r\n dpeFieldVisibility,\r\n} from '../../core/dpe-conditional-fields';\r\nimport {\r\n DpeNodeCollectionName,\r\n JoinKey,\r\n JoinKind,\r\n JoinNode,\r\n JoinResultField,\r\n} from '../../model/dpe-definition.model';\r\nimport { DpeSelectValueDirective } from '../shared/dpe-select-value.directive';\r\nimport { DpeNodeInspectorBase } from './dpe-node-inspector-base';\r\n\r\nconst KINDS: readonly JoinKind[] = ['Inner', 'LeftOuter', 'RightOuter', 'FullOuter', 'Lookup'];\r\n\r\n@Component({\r\n selector: 'dpe-join-inspector',\r\n standalone: true,\r\n imports: [DpeSelectValueDirective],\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n template: `\r\n <div class=\"dpe-form\">\r\n <div class=\"dpe-field\">\r\n <label class=\"dpe-field__label\" for=\"join-kind\">Tipo di join</label>\r\n <!--\r\n Il bordo del rilievo anche qui: e’ la tendina che porta il caso piu’ visibile di tutti —\r\n un FullOuter su SQLite — e senza aria-invalid sarebbe l’unico controllo diagnosticato\r\n senza il segno che l’utente ha imparato a cercare sui campi di testo.\r\n -->\r\n <select\r\n id=\"join-kind\"\r\n [dpeValue]=\"node().kind\"\r\n [attr.aria-invalid]=\"hasIssueOn('kind')\"\r\n (change)=\"setField('kind', valueOf($event))\"\r\n >\r\n @for (kind of kinds; track $index) {\r\n <option [value]=\"kind\">{{ kind }}</option>\r\n }\r\n </select>\r\n @for (issue of issuesOn('kind'); track $index) {\r\n <!--\r\n Un FullOuter su SQLite e’ DPE_DIALECT_UNSUPPORTED: il messaggio arriva da qui e non\r\n da un controllo locale, perche’ le capacita’ del dialetto le conosce il backend.\r\n -->\r\n <p class=\"dpe-field__issue\">{{ issue.code }} — {{ issue.message }}</p>\r\n }\r\n </div>\r\n\r\n <div class=\"dpe-row\">\r\n <div class=\"dpe-field\">\r\n <span class=\"dpe-field__label\">Sorgente primaria</span>\r\n <p class=\"dpe-field__hint\">{{ node().primarySourceName || 'non collegata' }}</p>\r\n </div>\r\n <div class=\"dpe-field\">\r\n <span class=\"dpe-field__label\">Sorgente secondaria</span>\r\n <p class=\"dpe-field__hint\">{{ node().secondarySourceName || 'non collegata' }}</p>\r\n </div>\r\n </div>\r\n <p class=\"dpe-field__hint\">Le sorgenti si collegano sul canvas, sulle due porte in alto.</p>\r\n\r\n <!--\r\n frontend.md §5-ter: la casella si rende anche fuori da Lookup quando il documento dichiara\r\n la spunta. Fuori da Lookup il backend non dice niente — il campo e’ semplicemente\r\n **ignorato** — ed e’ proprio per questo che deve restare visibile: nessuno lo segnala, e\r\n tornare a Lookup lo rimetterebbe in gioco. Spenta toglie la chiave, altrimenti il\r\n controllo non saprebbe dire «assente».\r\n -->\r\n @if (shows.allowsMultipleLookupValues()) {\r\n <label class=\"dpe-check\">\r\n <input\r\n type=\"checkbox\"\r\n [checked]=\"node().allowsMultipleLookupValues ?? false\"\r\n (change)=\"setFlag('allowsMultipleLookupValues', checkedOf($event))\"\r\n />\r\n Ammetti piu’ corrispondenze sul lato secondario\r\n </label>\r\n }\r\n\r\n <section class=\"dpe-section\">\r\n <h4 class=\"dpe-section__title\">Chiavi</h4>\r\n @if (keys().length === 0) {\r\n <p class=\"dpe-empty\">Nessuna chiave.</p>\r\n } @else {\r\n <table class=\"dpe-table\">\r\n <thead>\r\n <tr>\r\n <th>Campo primario</th>\r\n <th>Campo secondario</th>\r\n <th></th>\r\n </tr>\r\n </thead>\r\n <tbody>\r\n @for (key of keys(); track $index) {\r\n <tr>\r\n <td>\r\n <input\r\n type=\"text\"\r\n list=\"join-primary-columns\"\r\n [value]=\"key.primaryFieldName\"\r\n [attr.aria-label]=\"'Campo primario della chiave ' + ($index + 1)\"\r\n (change)=\"patchKey($index, { primaryFieldName: valueOf($event) })\"\r\n />\r\n </td>\r\n <td>\r\n <input\r\n type=\"text\"\r\n list=\"join-secondary-columns\"\r\n [value]=\"key.secondaryFieldName\"\r\n [attr.aria-label]=\"'Campo secondario della chiave ' + ($index + 1)\"\r\n (change)=\"patchKey($index, { secondaryFieldName: valueOf($event) })\"\r\n />\r\n </td>\r\n <td>\r\n <button\r\n type=\"button\"\r\n class=\"dpe-btn dpe-btn--icon dpe-btn--danger\"\r\n aria-label=\"Rimuovi la chiave\"\r\n (click)=\"removeKey($index)\"\r\n >\r\n ×\r\n </button>\r\n </td>\r\n </tr>\r\n }\r\n </tbody>\r\n </table>\r\n }\r\n <div>\r\n <button type=\"button\" class=\"dpe-btn\" (click)=\"addKey()\">Aggiungi chiave</button>\r\n </div>\r\n </section>\r\n\r\n <section class=\"dpe-section\">\r\n <h4 class=\"dpe-section__title\">Campi in uscita</h4>\r\n <p class=\"dpe-field__hint\">\r\n Lo schema del join e’ <strong>solo</strong> questo elenco: cio’ che non e’ dichiarato\r\n qui non arriva a valle.\r\n </p>\r\n @if (fields().length === 0) {\r\n <p class=\"dpe-empty\">Nessun campo dichiarato.</p>\r\n } @else {\r\n <table class=\"dpe-table\">\r\n <thead>\r\n <tr>\r\n <th>Da</th>\r\n <th>Campo</th>\r\n <th>Alias</th>\r\n <th></th>\r\n </tr>\r\n </thead>\r\n <tbody>\r\n @for (field of fields(); track $index) {\r\n <tr>\r\n <td>\r\n <!--\r\n Un elenco chiuso alle due sorgenti del nodo: il contratto dice che\r\n la sorgente del campo deve essere una delle due, e con una casella di testo\r\n digitarne una terza sarebbe possibile.\r\n -->\r\n <select\r\n [dpeValue]=\"field.sourceName\"\r\n [attr.aria-label]=\"'Sorgente del campo ' + ($index + 1)\"\r\n (change)=\"patchField($index, { sourceName: valueOf($event) })\"\r\n >\r\n <option value=\"\">—</option>\r\n @if (node().primarySourceName) {\r\n <option [value]=\"node().primarySourceName!\">\r\n {{ node().primarySourceName }} (primaria)\r\n </option>\r\n }\r\n @if (node().secondarySourceName) {\r\n <option [value]=\"node().secondarySourceName!\">\r\n {{ node().secondarySourceName }} (secondaria)\r\n </option>\r\n }\r\n </select>\r\n </td>\r\n <td>\r\n <input\r\n type=\"text\"\r\n [attr.list]=\"columnListIdFor(field)\"\r\n [value]=\"field.sourceFieldName\"\r\n [attr.aria-label]=\"'Campo ' + ($index + 1)\"\r\n (change)=\"patchField($index, { sourceFieldName: valueOf($event) })\"\r\n />\r\n </td>\r\n <td>\r\n <input\r\n type=\"text\"\r\n [value]=\"field.alias\"\r\n [attr.aria-label]=\"'Alias del campo ' + ($index + 1)\"\r\n (change)=\"patchField($index, { alias: valueOf($event) })\"\r\n />\r\n </td>\r\n <td>\r\n <button\r\n type=\"button\"\r\n class=\"dpe-btn dpe-btn--icon dpe-btn--danger\"\r\n aria-label=\"Rimuovi il campo\"\r\n (click)=\"removeField($index)\"\r\n >\r\n ×\r\n </button>\r\n </td>\r\n </tr>\r\n }\r\n </tbody>\r\n </table>\r\n }\r\n <div>\r\n <button type=\"button\" class=\"dpe-btn\" (click)=\"addField()\">Aggiungi campo</button>\r\n </div>\r\n </section>\r\n\r\n <datalist id=\"join-primary-columns\">\r\n @for (column of primaryColumns(); track $index) {\r\n <option [value]=\"column.name\">{{ column.type.kind }}</option>\r\n }\r\n </datalist>\r\n <datalist id=\"join-secondary-columns\">\r\n @for (column of secondaryColumns(); track $index) {\r\n <option [value]=\"column.name\">{{ column.type.kind }}</option>\r\n }\r\n </datalist>\r\n </div>\r\n `,\r\n})\r\nexport class DpeJoinInspectorComponent extends DpeNodeInspectorBase<JoinNode> {\r\n override readonly collection: DpeNodeCollectionName = 'joins';\r\n\r\n protected readonly kinds = KINDS;\r\n\r\n readonly keys = computed<readonly JoinKey[]>(() => this.node().keys ?? []);\r\n readonly fields = computed<readonly JoinResultField[]>(() => this.node().fields ?? []);\r\n\r\n /**\r\n * L'unico campo condizionale di questo nodo, nella forma di `frontend.md` §5-ter: **la\r\n * condizione vale oppure il campo porta un valore**. `false` vale assenza (`dpeDeclares`),\r\n * quindi una giunzione che non dichiara niente non guadagna una casella.\r\n */\r\n readonly shows = dpeFieldVisibility(this.node, DPE_JOIN_CONDITIONS);\r\n\r\n /** Gli schemi dei due lati, separati: qui `inputColumns` fusi non basterebbero. */\r\n readonly primaryColumns = computed(\r\n () => this.store.schemaByNode().get(this.node().primarySourceName ?? '')?.columns ?? [],\r\n );\r\n\r\n readonly secondaryColumns = computed(\r\n () => this.store.schemaByNode().get(this.node().secondarySourceName ?? '')?.columns ?? [],\r\n );\r\n\r\n /** L'elenco dei campi proposti segue il lato scelto sulla riga. */\r\n protected columnListIdFor(field: JoinResultField): string {\r\n return field.sourceName === this.node().secondarySourceName\r\n ? 'join-secondary-columns'\r\n : 'join-primary-columns';\r\n }\r\n\r\n protected addKey(): void {\r\n this.patch({ keys: [...this.keys(), { primaryFieldName: '', secondaryFieldName: '' }] });\r\n }\r\n\r\n protected removeKey(index: number): void {\r\n this.patch({ keys: this.keys().filter((_, position) => position !== index) });\r\n }\r\n\r\n protected patchKey(index: number, patch: Partial<JoinKey>): void {\r\n this.patch({\r\n keys: this.keys().map((key, position) => (position === index ? { ...key, ...patch } : key)),\r\n });\r\n }\r\n\r\n protected addField(): void {\r\n this.patch({\r\n fields: [\r\n ...this.fields(),\r\n { sourceName: this.node().primarySourceName ?? '', sourceFieldName: '', alias: '' },\r\n ],\r\n });\r\n }\r\n\r\n protected removeField(index: number): void {\r\n this.patch({ fields: this.fields().filter((_, position) => position !== index) });\r\n }\r\n\r\n protected patchField(index: number, patch: Partial<JoinResultField>): void {\r\n this.patch({\r\n fields: this.fields().map((field, position) =>\r\n position === index ? { ...field, ...patch } : field,\r\n ),\r\n });\r\n }\r\n}\r\n","/**\r\n * L'editor di un'espressione.\r\n *\r\n * **Le espressioni non sono piu' SQL: sono formule** (ADR 0031), e le compila un componente\r\n * registrato dall'ospite. L'editor non conosce il linguaggio, non lo valida e non lo compila:\r\n * mostra, manda, e rende cio' che torna.\r\n *\r\n * Da qui la scomparsa dell'avviso di sicurezza che stava accanto al campo. Diceva che scrivere\r\n * un'espressione equivale a eseguire SQL arbitrario sul database, ed era vero sotto l'ADR 0006:\r\n * con le formule non lo e' piu', e cio' che raggiunge il database e' cio' che il compilatore\r\n * emette. Lasciarlo sarebbe dire il falso nel punto esatto in cui l'utente lo legge — il difetto\r\n * peggiore che un avviso possa avere.\r\n *\r\n * Al suo posto tre cose, e nessuna e' una regola di dominio:\r\n *\r\n * 1. **l'etichetta nomina il linguaggio**, non il dialetto: il dialetto non c'entra piu' con cio'\r\n * che si scrive qui. Il nome arriva da `DpeEnvironment.expressionLanguage` e **non** dal\r\n * catalogo, perche' il catalogo puo' non arrivare (compilatore che non lo fornisce, dialetto\r\n * non coperto) proprio quando serve sapere in che lingua si sta scrivendo;\r\n * 2. **le diagnostiche del backend accanto al campo**, con il loro codice e il loro messaggio. E'\r\n * la ragione per cui esiste un compilatore di formule: sa spiegare un errore. Il messaggio\r\n * porta il testo dell'ospite verbatim e la posizione quando c'e' («dal carattere 4 per 2\r\n * caratteri: Funzione 'SOMMA' sconosciuta: forse intendevi 'SUM'?»), e l'editor **non lo\r\n * ricompone** — lo rende;\r\n * 3. **cosa il linguaggio mette a disposizione in questo nodo**, come conteggio. Un conteggio e\r\n * non un elenco: la palette delle funzioni con firma e descrizione, l'elenco dei simboli e\r\n * l'inserimento al cursore sono un lotto a parte (ADR 0036 §2), e non bloccano niente.\r\n *\r\n * I **tre esiti** del catalogo si rendono diversi perche' sono diversi (`contracts.md` §7):\r\n * tavolozza, fallimento con la riga in testa che nomina la sorgente, catalogo consegnato vuoto\r\n * senza nessun rumore. Prima ne arrivavano due — un catalogo oppure un errore di trasporto — e\r\n * *dialetto non coperto* e *backend rotto* si vedevano identici, cioe' l'editor degradava in\r\n * silenzio. La distinzione la fa `dpeFormulaCatalogOutcome`, non un `if` qui dentro.\r\n *\r\n * La **forma pretesa** dal campo si mostra accanto all'etichetta e arriva dal backend. Non si\r\n * deduce dal tipo di nodo: sarebbe una regola di dominio in TypeScript, che e' il confine su cui\r\n * questo componente e' costruito.\r\n *\r\n * Arrivando dal backend, la forma ha una **finestra di ritardo**: fra il gesto che la cambia e la\r\n * risposta della validazione la pastiglia mostra quella precedente. Per questo, mentre una\r\n * validazione e' in volo, si rende **«in aggiornamento» e non asserita** (ADR 0042, `isFormUpdating`).\r\n * Il **blocco** per forma non supportata invece **resta applicato** — si ammorbidisce\r\n * l'affermazione, non la protezione: rilasciarlo inviterebbe a scrivere in un campo che sta per\r\n * essere bloccato, e l'input verrebbe congelato a meta' parola.\r\n *\r\n * E quando quella forma **non e'** fra quelle che il compilatore registrato sa produrre\r\n * (`DpeEnvironment.supportedForms`), il campo **non si scrive e dice perche'**. Le due meta'\r\n * arrivano da due posti — la forma dallo **schema** del nodo, le capacita' dall'ambiente — e\r\n * l'editor le incrocia in `dpeExpressionFormSupport`, funzione pura e non un `if` qui dentro.\r\n * Fino a quando il secondo operando non stava sul filo, questo campo sapeva dire cosa serve ma\r\n * non che non si potra' avere: l'utente scriveva una formula destinata a non compilare mai.\r\n *\r\n * **Capacita' non dichiarate non disabilitano niente**: non sapere non autorizza a indovinare.\r\n *\r\n * **Nessuna validazione di formula in TypeScript, per nessun motivo** — nemmeno «solo le\r\n * parentesi». La sola autorita' sul linguaggio e' il compilatore dell'ospite; un controllo locale\r\n * sarebbe una regola di dominio nel posto sbagliato, piu' una che diverge il giorno in cui il\r\n * linguaggio cambia.\r\n */\r\n\r\nimport { ChangeDetectionStrategy, Component, computed, input, output } from '@angular/core';\r\n\r\nimport { DpeExpressionForm } from '../../model/dpe-api.model';\r\nimport {\r\n DpeExpressionFormSupport,\r\n DpeFormulaCatalogOutcome,\r\n dpeExpressionFormPrompt,\r\n dpeFormulaCatalogSize,\r\n} from '../../core/dpe-formula-catalog.view';\r\nimport { Diagnostic } from '../../model/dpe-diagnostics.model';\r\n\r\n@Component({\r\n selector: 'dpe-expression-editor',\r\n standalone: true,\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n template: `\r\n <div class=\"dpe-field\">\r\n <!--\r\n Due pastiglie e non una: il **linguaggio** dice in che lingua si scrive, la **forma** dice\r\n che cosa il compilatore si aspetta qui. La seconda arriva dal backend\r\n (DpeNodeSchemaInfo.requiredForm, cioe’ con la validazione) e non si deduce dal tipo di nodo\r\n — dedurla sarebbe una regola di dominio in TypeScript.\r\n -->\r\n <label class=\"dpe-field__label\" [attr.for]=\"controlId()\">\r\n {{ label() }}\r\n @if (language(); as name) {\r\n <span class=\"dpe-expr__language\">{{ name }}</span>\r\n }\r\n <!--\r\n **Mentre la validazione e’ in volo la pastiglia non si afferma** (ADR 0042). La forma\r\n arriva da validate, quindi fra il gesto che la cambia — il tipo di una trasformazione —\r\n e la risposta c’e’ una finestra in cui questa pastiglia mostra la forma **precedente**\r\n con la faccia di un fatto. Misurata a 2,58 s dentro la pagina, ed esiste per costruzione:\r\n su un ospite vero il numero cambia, la finestra no.\r\n\r\n Non si spegne e non sparisce: si segna «in aggiornamento», cioe’ resta leggibile e smette\r\n di asserire. L’attributo aria-busy lo dice a chi non vede il colore.\r\n -->\r\n @if (formPrompt(); as prompt) {\r\n <span [class]=\"formClass()\" [title]=\"formTitle()\" [attr.aria-busy]=\"ariaBusy()\"\r\n >{{ prompt }}@if (isFormUpdating()) {\r\n <span class=\"dpe-expr__form-note\"> · in aggiornamento</span>\r\n }</span\r\n >\r\n }\r\n </label>\r\n <!--\r\n **La forma richiesta non e’ fra quelle che il compilatore sa produrre**: il campo non si\r\n scrive, e il perche’ sta sotto.\r\n\r\n Non «disabled» ma «readonly» piu’ «aria-disabled», ed e’ una scelta: un controllo\r\n disabilitato esce dall’ordine di tabulazione e il suo testo non si seleziona, cioe’ chi\r\n naviga da tastiera non raggiunge il campo e chi ha gia’ scritto una formula non puo’ piu’\r\n copiarla — proprio nel caso in cui gli servirebbe, perche’ quella formula va portata\r\n altrove. L’effetto voluto e’ «non si scrive», e la sola lettura lo ottiene per intero.\r\n -->\r\n <textarea\r\n [id]=\"controlId()\"\r\n [class]=\"inputClass()\"\r\n [value]=\"value() ?? ''\"\r\n [readOnly]=\"isBlocked()\"\r\n [attr.aria-disabled]=\"isBlocked() ? 'true' : null\"\r\n [attr.placeholder]=\"placeholder()\"\r\n [attr.aria-describedby]=\"describedBy()\"\r\n [attr.aria-invalid]=\"hasError() ? 'true' : null\"\r\n spellcheck=\"false\"\r\n (change)=\"valueChange.emit(text($event))\"\r\n ></textarea>\r\n\r\n @if (blockedReason(); as reason) {\r\n <p class=\"dpe-note dpe-note--not-compilable dpe-expr__blocked\" [id]=\"controlId() + '-blocked'\">\r\n {{ reason }}\r\n </p>\r\n }\r\n\r\n <!--\r\n Il rilievo del compilatore, **verbatim**. Il track per indice non e’ pigrizia: le chiavi\r\n composte da codice e percorso si duplicano davvero su due campi della stessa riga, e\r\n Angular tratta la chiave doppia come errore (NG0955).\r\n\r\n Nessuna classe statica accanto al binding di class: il binding a stringa governa l’intera\r\n lista, e una classe scritta anche nell’attributo statico sembra ridondante finche’ non\r\n sparisce.\r\n -->\r\n <div [id]=\"controlId() + '-issues'\">\r\n @for (issue of issues(); track $index) {\r\n <p [class]=\"issueClass(issue)\">{{ issue.code }} — {{ issue.message }}</p>\r\n }\r\n </div>\r\n\r\n <!--\r\n **Il catalogo non e' stato prodotto, e il perche' si vede.** Non e' un rilievo sulla\r\n formula: e’ un rilievo sul servizio che descrive il linguaggio, quindi sta in un blocco\r\n suo e non fra i rilievi del compilatore, dove l’utente lo leggerebbe come «ho sbagliato a\r\n scrivere».\r\n\r\n La riga in testa e’ DPE_CATALOG_UNAVAILABLE quando c’e’: nomina la sorgente su cui agire.\r\n Le strutturali che seguono ne dicono la causa e stanno in un dettaglio richiudibile — non\r\n si filtrano per pertinenza (l’errore che spiega lo schema puo’ stare a piu’ salti di\r\n distanza), quindi si dichiarano come dettaglio invece di diventare rumore.\r\n -->\r\n @if (failure(); as outcome) {\r\n <div [class]=\"failureClass()\" [id]=\"controlId() + '-catalog'\">\r\n <p class=\"dpe-expr__catalog-lead\">\r\n {{ outcome.lead.code }} — {{ outcome.lead.message }}\r\n </p>\r\n @if (outcome.details.length > 0) {\r\n <details class=\"dpe-expr__catalog-details\">\r\n <summary>\r\n Dettaglio: {{ outcome.details.length }} rilievi strutturali della definizione\r\n </summary>\r\n @for (detail of outcome.details; track $index) {\r\n <p [class]=\"issueClass(detail)\">{{ detail.code }} — {{ detail.message }}</p>\r\n }\r\n </details>\r\n }\r\n </div>\r\n }\r\n\r\n <p class=\"dpe-field__hint\" [id]=\"controlId() + '-hint'\">\r\n @if (columnHint().length > 0) {\r\n Colonne in ingresso: {{ columnHint() }}\r\n } @else {\r\n Le colonne in ingresso arrivano dalla validazione.\r\n }\r\n <!--\r\n Il conteggio compare **solo** con una tavolozza vera. Il terzo esito — catalogo\r\n consegnato vuoto perche’ il compilatore non descrive il proprio linguaggio — non e’ un\r\n guasto e non merita nessuna riga: niente tavolozza e **nessun rumore**.\r\n -->\r\n @if (isCatalogLoading()) {\r\n · <span class=\"dpe-expr__catalog\">lettura del catalogo…</span>\r\n } @else if (catalogHint(); as hint) {\r\n · <span class=\"dpe-expr__catalog\">{{ hint }}</span>\r\n }\r\n </p>\r\n </div>\r\n `,\r\n})\r\nexport class DpeExpressionEditorComponent {\r\n readonly label = input('Espressione');\r\n readonly value = input<string | undefined>(undefined);\r\n readonly placeholder = input('');\r\n /**\r\n * Il nome del linguaggio di formule dichiarato dall'ambiente. Assente: l'etichetta non nomina\r\n * niente. **Non** ripiega sul dialetto — sarebbe la bugia che questo lotto e' venuto a togliere.\r\n */\r\n readonly language = input<string | undefined>(undefined);\r\n /**\r\n * I nomi delle colonne a monte, **come suggerimento**: sono cio' che il backend ha propagato\r\n * (**I4**), e l'editor non li calcola.\r\n */\r\n readonly columns = input<readonly string[]>([]);\r\n /**\r\n * Le diagnostiche del backend **su questo campo**, gia' filtrate da chi conosce il percorso.\r\n * Si rendono e basta: il messaggio lo compone il backend, che e' l'unico posto in cui un\r\n * messaggio viene composto.\r\n */\r\n readonly issues = input<readonly Diagnostic[]>([]);\r\n /**\r\n * **Quale dei tre esiti del catalogo** si sta guardando, gia' letto da `dpeFormulaCatalogOutcome`.\r\n *\r\n * Prende l'esito e non il catalogo perche' il catalogo da solo non basta piu' a distinguerli: un\r\n * compilatore che non descrive il proprio linguaggio consegna il catalogo **presente e vuoto**,\r\n * quindi «assente» e «vuoto» sono due situazioni diverse che un solo oggetto nullabile\r\n * appiattiva — ed e' l'appiattimento che faceva sembrare uguali un dialetto non coperto e un\r\n * backend rotto.\r\n */\r\n readonly outcome = input<DpeFormulaCatalogOutcome>({ kind: 'Unknown' });\r\n /**\r\n * La forma pretesa dal campo, **dal backend**. Assente: il campo non la nomina. Non si deduce\r\n * dal tipo di nodo, che sarebbe una regola di dominio scritta dalla parte sbagliata del confine.\r\n */\r\n readonly requiredForm = input<DpeExpressionForm | undefined>(undefined);\r\n /**\r\n * L'incrocio **gia' fatto** fra la forma richiesta da questo nodo e le forme che il compilatore\r\n * registrato sa produrre (`DpeEnvironment.supportedForms`), come per `outcome`: il componente\r\n * riceve l'esito, non i due operandi.\r\n *\r\n * Il difetto predefinito e' «supportato», ed e' la politica del contratto: **capacita' non\r\n * dichiarate non disabilitano niente**. Un ospite che monta questo componente senza passare\r\n * nulla ottiene un campo che si scrive, non uno spento per ignoranza.\r\n */\r\n readonly formSupport = input<DpeExpressionFormSupport>({ isSupported: true });\r\n /**\r\n * **Una validazione e' in volo**, quindi la forma qui sopra puo' non essere ancora quella del\r\n * documento che l'utente ha davanti (ADR 0042). La pastiglia si segna «in aggiornamento» e\r\n * smette di asserire; il **blocco resta applicato**.\r\n *\r\n * E' di **documento** e **transitorio**, e per questo non e' una regola di dominio: arriva da\r\n * `DpeValidationService.isValidating`, che esisteva gia'. L'editor non sa — e non deve sapere —\r\n * che la forma dipende dal tipo del nodo: saperlo sarebbe riscrivere in TypeScript la regola che\r\n * `requiredForm` attraversa il confine per non avere.\r\n *\r\n * Non e' «il documento e' cambiato dall'ultima validazione», che e' il rimedio che l'ADR 0042\r\n * rifiuta: quello sarebbe vero **per tutta** la digitazione e spegnerebbe la pastiglia proprio\r\n * mentre si scrive nel campo che accompagna. `isValidating` diventa vero solo dopo la pausa di\r\n * digitazione, quindi mentre si scrive questa pastiglia resta ferma — e corretta, perche' la\r\n * forma non e' cambiata.\r\n *\r\n * Difetto predefinito `false`: un ospite che monta il componente senza passare nulla ottiene una\r\n * pastiglia asserita, che e' cio' che era prima.\r\n */\r\n readonly isFormUpdating = input(false);\r\n readonly isCatalogLoading = input(false);\r\n readonly controlId = input('dpe-expression');\r\n\r\n readonly valueChange = output<string>();\r\n\r\n readonly hasError = computed(() => this.issues().some((issue) => issue.severity === 'Error'));\r\n\r\n /** Elenco troncato: dieci nomi sono un aiuto, cinquanta sono un muro di testo. */\r\n readonly columnHint = computed(() => {\r\n const columns = this.columns();\r\n const shown = columns.slice(0, 10).join(', ');\r\n return columns.length > 10 ? `${shown}, …` : shown;\r\n });\r\n\r\n /**\r\n * La forma pretesa, detta all'utente. Assente quando il nodo non ospita formule: il campo non\r\n * nomina nessuna forma di ripiego, che sarebbe la deduzione vietata travestita da default.\r\n */\r\n readonly formPrompt = computed(() => dpeExpressionFormPrompt(this.requiredForm()));\r\n\r\n /**\r\n * La classe della pastiglia. Binding a stringa e nessuna classe statica accanto, come per il\r\n * controllo bloccato: il binding governa l'intera lista.\r\n *\r\n * Il modificatore non cambia il testo, cambia il **tono**: la pastiglia perde la tinta d'accento\r\n * — che e' cio' che la faceva leggere come un fatto corrente — e prende il bordo tratteggiato\r\n * della cosa provvisoria.\r\n */\r\n readonly formClass = computed(() =>\r\n this.isFormUpdating() ? 'dpe-expr__form dpe-expr__form--updating' : 'dpe-expr__form',\r\n );\r\n\r\n /**\r\n * Il titolo dice **cosa aspettarsi**, e nella finestra di aggiornamento dice anche che il blocco\r\n * non si tocca: e' la meta' che l'utente non puo' dedurre dal colore, e senza la quale un campo\r\n * che non accetta testo accanto a un'etichetta incerta sembra rotto.\r\n */\r\n readonly formTitle = computed(() =>\r\n this.isFormUpdating()\r\n ? 'Una validazione e’ in corso: questa forma potrebbe non essere ancora aggiornata. ' +\r\n 'Il blocco del campo resta applicato finche’ la risposta non arriva.'\r\n : 'La forma che il compilatore si aspetta in questo campo, dichiarata dal backend.',\r\n );\r\n\r\n /** `aria-busy` invece del solo colore: chi usa un lettore di schermo deve sentirlo. */\r\n readonly ariaBusy = computed(() => (this.isFormUpdating() ? 'true' : null));\r\n\r\n /** Il campo non si scrive: la forma richiesta qui non e' fra quelle che il compilatore produce. */\r\n readonly isBlocked = computed(() => !this.formSupport().isSupported);\r\n\r\n /**\r\n * Il motivo, **presente solo quando il campo e' bloccato**. Un campo inerte e muto fa cercare\r\n * all'utente un errore nella propria formula, che e' l'unico posto in cui il problema non sta.\r\n */\r\n readonly blockedReason = computed(() =>\r\n this.formSupport().isSupported ? undefined : this.formSupport().reason,\r\n );\r\n\r\n /**\r\n * La classe del controllo bloccato. Binding a stringa e nessuna classe statica accanto, come per\r\n * i rilievi: il binding governa l'intera lista e una classe scritta anche nell'attributo statico\r\n * sembra ridondante finche' non sparisce.\r\n */\r\n readonly inputClass = computed(() => (this.isBlocked() ? 'dpe-expr__input--blocked' : ''));\r\n\r\n /** L'esito di fallimento, quando e' quello: il template rende solo se c'e'. */\r\n readonly failure = computed(() => {\r\n const outcome = this.outcome();\r\n return outcome.kind === 'Failed' ? outcome : undefined;\r\n });\r\n\r\n /**\r\n * Il blocco del fallimento prende il bordo dell'errore quando fra le diagnostiche c'e' un\r\n * `Error`, quello dell'avviso altrimenti. Non e' decorazione: «il catalogo non c'e' perche' la\r\n * definizione e' rotta a monte» e «il catalogo non c'e' e basta» si correggono in modi diversi.\r\n */\r\n readonly failureClass = computed(() =>\r\n this.failure()?.hasError ? 'dpe-note dpe-note--danger' : 'dpe-note dpe-note--warning',\r\n );\r\n\r\n /**\r\n * Cosa il linguaggio offre **qui**, in numeri. E' deliberatamente un conteggio: dice che il\r\n * catalogo e' arrivato e che dipende dal nodo (i simboli cambiano con lo schema a monte), senza\r\n * anticipare l'elenco navigabile, che e' un componente e va progettato guardandolo.\r\n *\r\n * Solo per l'esito `Palette`: un conteggio di zeri sotto un campo direbbe che qualcosa e'\r\n * andato storto, mentre il compilatore ha soltanto scelto di non descriversi.\r\n */\r\n readonly catalogHint = computed<string | undefined>(() => {\r\n const outcome = this.outcome();\r\n if (outcome.kind !== 'Palette') {\r\n return undefined;\r\n }\r\n const catalog = outcome.catalog;\r\n return (\r\n `${catalog.functions.length} funzioni, ${catalog.operators.length} operatori, ` +\r\n `${catalog.symbols.length} simboli disponibili (${dpeFormulaCatalogSize(catalog)} voci)`\r\n );\r\n });\r\n\r\n /**\r\n * `aria-describedby` porta **il suggerimento, i rilievi e il motivo per cui la tavolozza manca**:\r\n * un lettore di schermo deve sentire l'errore del compilatore leggendo il campo, non cercandolo\r\n * piu' in basso, e deve sapere che qui non c'e' aiuto disponibile prima di scrivere.\r\n */\r\n readonly describedBy = computed(() => {\r\n const id = this.controlId();\r\n const parts: string[] = [];\r\n // Per primo il motivo del blocco quando c'e': e' l'unica cosa che cambia cosa si puo' fare\r\n // qui, e sentirlo dopo tre rilievi significa averli ascoltati per niente.\r\n if (this.isBlocked()) {\r\n parts.push(`${id}-blocked`);\r\n }\r\n if (this.issues().length > 0) {\r\n parts.push(`${id}-issues`);\r\n }\r\n if (this.failure()) {\r\n parts.push(`${id}-catalog`);\r\n }\r\n parts.push(`${id}-hint`);\r\n return parts.join(' ');\r\n });\r\n\r\n protected issueClass(issue: Diagnostic): string {\r\n if (issue.severity === 'Warning') {\r\n return 'dpe-field__issue dpe-field__issue--warning';\r\n }\r\n if (issue.severity === 'Info') {\r\n return 'dpe-field__issue dpe-field__issue--info';\r\n }\r\n return 'dpe-field__issue';\r\n }\r\n\r\n protected text(event: Event): string {\r\n return (event.target as HTMLTextAreaElement).value;\r\n }\r\n}\r\n","/**\r\n * Ispettore della trasformazione.\r\n *\r\n * `kind` governa i campi, e la corrispondenza e' quella del contratto: `expressionFields` per\r\n * `Expression` e `ComputeRelative`, `droppedFields` per `Slice`, `partitionBy`/`orderBy` solo\r\n * per `ComputeRelative`. Il vincolo e' **esclusivo** in entrambi i sensi: per `Slice` le\r\n * espressioni devono essere vuote.\r\n *\r\n * Quelle condizioni non sono scritte qui: stanno in `DPE_TRANSFORM_CONDITIONS`\r\n * (`core/dpe-conditional-fields.ts`), **un punto solo** letto sia dalla resa sia dalla ripulitura.\r\n * Prima erano due copie a pochi metodi di distanza in questo stesso file, e una divergenza fra le\r\n * due non l'avrebbe vista nessun test.\r\n *\r\n * Cambiare `kind` **ripulisce** i campi che il tipo nuovo non governa, ed e' la stessa decisione\r\n * del cambio di destinazione su un writeback (`frontend.md` §5-ter, ultima parte): scegliere il\r\n * tipo e' un gesto deliberato con cui l'autore dichiara inapplicabili i campi del tipo precedente,\r\n * e ripulire esegue quell'intento invece di lasciargli un rilievo per campo da spegnere a mano.\r\n * Due comportamenti diversi per la stessa situazione su due tipi di nodo diventerebbero folklore,\r\n * e il folklore non si documenta.\r\n *\r\n * Ripulire e' **togliere la chiave**, non scriverci un valore vuoto, come fa `setFlag()`: un\r\n * `droppedFields: []` sarebbe una collezione a nome di nessuno nel documento (**I7**).\r\n *\r\n * La ripulitura non rende inutile l'altra meta', che e' normativa (`frontend.md` §5-ter): **un\r\n * campo che porta un valore si rende anche fuori dalla propria condizione**. Le due regole si\r\n * dividono il lavoro — questa copre le modifiche fatte qui, quella copre i documenti scritti\r\n * altrove, che sono l'unico modo rimasto di far arrivare un valore fuori condizione.\r\n */\r\n\r\nimport { ChangeDetectionStrategy, Component, computed, effect, inject } from '@angular/core';\r\n\r\nimport {\r\n DPE_TRANSFORM_CONDITIONS,\r\n dpeFieldVisibility,\r\n} from '../../core/dpe-conditional-fields';\r\nimport { DpeFormulaCatalogStore } from '../../core/dpe-formula-catalog.store';\r\nimport { dpeExpressionFormSupport } from '../../core/dpe-formula-catalog.view';\r\nimport { DpeValidationService } from '../../core/dpe-validation.service';\r\nimport {\r\n DpeNodeCollectionName,\r\n ExpressionField,\r\n OrderByField,\r\n TransformKind,\r\n TransformNode,\r\n} from '../../model/dpe-definition.model';\r\nimport { DpeColumnTypeEditorComponent } from '../shared/dpe-column-type-editor.component';\r\nimport { DpeExpressionEditorComponent } from '../shared/dpe-expression-editor.component';\r\nimport { DpeSelectValueDirective } from '../shared/dpe-select-value.directive';\r\nimport { DpeNodeInspectorBase } from './dpe-node-inspector-base';\r\n\r\nconst KINDS: readonly TransformKind[] = ['Expression', 'Slice', 'ComputeRelative'];\r\n\r\n@Component({\r\n selector: 'dpe-transform-inspector',\r\n standalone: true,\r\n imports: [DpeColumnTypeEditorComponent, DpeExpressionEditorComponent, DpeSelectValueDirective],\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n template: `\r\n <div class=\"dpe-form\">\r\n <div class=\"dpe-field\">\r\n <label class=\"dpe-field__label\" for=\"tr-kind\">Tipo di trasformazione</label>\r\n <select id=\"tr-kind\" [dpeValue]=\"node().kind\" (change)=\"onKind(valueOf($event))\">\r\n @for (kind of kinds; track $index) {\r\n <option [value]=\"kind\">{{ kind }}</option>\r\n }\r\n </select>\r\n <!--\r\n L’avviso dice cosa succede **prima** che succeda, perche’ e’ una perdita di campi su un\r\n gesto di routine. Non nomina il ↶ della barra: con la finestra aperta quel bottone e’\r\n irraggiungibile, e una frase vera solo dopo la chiusura e’ il difetto appena corretto nel\r\n piede della dialog.\r\n -->\r\n <p class=\"dpe-field__hint\">\r\n Cambiare tipo toglie dal documento i campi che il tipo nuovo non governa. È una modifica\r\n come le altre: rientra nell’annullamento.\r\n </p>\r\n </div>\r\n\r\n <!--\r\n Due blocchi indipendenti e non un ramo alternativo: per frontend.md §5-ter, normativa,\r\n **un campo che porta un valore si rende anche fuori dalla propria condizione**. Da quando\r\n cambiare tipo ripulisce, il caso arriva solo da un documento scritto altrove — che e’\r\n precisamente il caso per cui quella regola esiste, ed e’ anche l’unico che nessuna\r\n ripulitura puo’ raggiungere. Vuoto e non applicabile resta nascosto.\r\n -->\r\n @if (shows.droppedFields()) {\r\n <div class=\"dpe-field\">\r\n <label class=\"dpe-field__label\" for=\"tr-dropped\">Colonne da eliminare</label>\r\n <input\r\n id=\"tr-dropped\"\r\n type=\"text\"\r\n [value]=\"droppedText()\"\r\n placeholder=\"separate da virgola\"\r\n (change)=\"onDropped(valueOf($event))\"\r\n />\r\n <p class=\"dpe-field__hint\">In ingresso: {{ columnHint() }}</p>\r\n </div>\r\n }\r\n\r\n @if (shows.expressionFields()) {\r\n <section class=\"dpe-section\">\r\n <h4 class=\"dpe-section__title\">Colonne calcolate</h4>\r\n @if (expressionFields().length === 0) {\r\n <p class=\"dpe-empty\">Nessuna colonna calcolata.</p>\r\n }\r\n @for (field of expressionFields(); track $index) {\r\n <div class=\"dpe-section\">\r\n <div class=\"dpe-row\">\r\n <div class=\"dpe-field\">\r\n <label class=\"dpe-field__label\" [attr.for]=\"'tr-alias-' + $index\">Alias</label>\r\n <input\r\n [id]=\"'tr-alias-' + $index\"\r\n type=\"text\"\r\n [value]=\"field.alias\"\r\n (change)=\"patchExpression($index, { alias: valueOf($event) })\"\r\n />\r\n </div>\r\n <button\r\n type=\"button\"\r\n class=\"dpe-btn dpe-btn--danger\"\r\n [attr.aria-label]=\"'Rimuovi la colonna ' + field.alias\"\r\n (click)=\"removeExpression($index)\"\r\n >\r\n Rimuovi\r\n </button>\r\n </div>\r\n\r\n <!--\r\n Le formule del nodo. Le diagnostiche si passano **per riga**: filtrate per solo\r\n nome di campo, l’errore della prima formula comparirebbe sotto la terza.\r\n -->\r\n <dpe-expression-editor\r\n label=\"Formula\"\r\n [value]=\"field.expression\"\r\n [language]=\"language()\"\r\n [columns]=\"columnNames()\"\r\n [issues]=\"issuesOnItem('expressionFields', $index, 'expression')\"\r\n [outcome]=\"catalogOutcome()\"\r\n [requiredForm]=\"requiredForm()\"\r\n [formSupport]=\"formSupport()\"\r\n [isFormUpdating]=\"isFormUpdating()\"\r\n [isCatalogLoading]=\"isCatalogLoading()\"\r\n [controlId]=\"'tr-expr-' + $index\"\r\n (valueChange)=\"patchExpression($index, { expression: $event })\"\r\n />\r\n\r\n <div class=\"dpe-field\">\r\n <span class=\"dpe-field__label\">Tipo dichiarato</span>\r\n <dpe-column-type-editor\r\n [value]=\"field.declaredType\"\r\n [controlId]=\"'tr-type-' + $index\"\r\n (valueChange)=\"patchExpression($index, { declaredType: $event })\"\r\n />\r\n <p class=\"dpe-field__hint\">\r\n <strong>Dichiarato dall’utente</strong>: lo schema resta una funzione della\r\n definizione, quindi la colonna calcolata ha il tipo scritto qui e non quello\r\n dedotto dal compilatore. Il compilatore lo <strong>verifica</strong> e lo segnala\r\n se non torna.\r\n </p>\r\n </div>\r\n </div>\r\n }\r\n <div>\r\n <button type=\"button\" class=\"dpe-btn\" (click)=\"addExpression()\">Aggiungi colonna</button>\r\n </div>\r\n </section>\r\n }\r\n\r\n @if (shows.partitionBy() || shows.orderBy()) {\r\n <section class=\"dpe-section\">\r\n <h4 class=\"dpe-section__title\">Finestra</h4>\r\n <div class=\"dpe-field\">\r\n <label class=\"dpe-field__label\" for=\"tr-partition\">Partiziona per</label>\r\n <input\r\n id=\"tr-partition\"\r\n type=\"text\"\r\n [value]=\"partitionText()\"\r\n placeholder=\"colonne separate da virgola\"\r\n (change)=\"onPartition(valueOf($event))\"\r\n />\r\n </div>\r\n <div class=\"dpe-field\">\r\n <span class=\"dpe-field__label\">Ordina per</span>\r\n @for (order of orderBy(); track $index) {\r\n <div class=\"dpe-row\">\r\n <input\r\n type=\"text\"\r\n list=\"tr-columns\"\r\n [value]=\"order.columnName\"\r\n [attr.aria-label]=\"'Colonna di ordinamento ' + ($index + 1)\"\r\n (change)=\"patchOrder($index, { columnName: valueOf($event) })\"\r\n />\r\n <select\r\n [dpeValue]=\"order.direction ?? 'Ascending'\"\r\n [attr.aria-label]=\"'Direzione ' + ($index + 1)\"\r\n (change)=\"patchOrder($index, { direction: valueOf($event) === 'Descending' ? 'Descending' : 'Ascending' })\"\r\n >\r\n <option value=\"Ascending\">Ascendente</option>\r\n <option value=\"Descending\">Discendente</option>\r\n </select>\r\n <button\r\n type=\"button\"\r\n class=\"dpe-btn dpe-btn--icon dpe-btn--danger\"\r\n aria-label=\"Rimuovi l’ordinamento\"\r\n (click)=\"removeOrder($index)\"\r\n >\r\n ×\r\n </button>\r\n </div>\r\n }\r\n <div>\r\n <button type=\"button\" class=\"dpe-btn\" (click)=\"addOrder()\">Aggiungi ordinamento</button>\r\n </div>\r\n </div>\r\n </section>\r\n }\r\n\r\n <datalist id=\"tr-columns\">\r\n @for (column of inputColumns(); track $index) {\r\n <option [value]=\"column.name\">{{ column.type.kind }}</option>\r\n }\r\n </datalist>\r\n </div>\r\n `,\r\n})\r\nexport class DpeTransformInspectorComponent extends DpeNodeInspectorBase<TransformNode> {\r\n override readonly collection: DpeNodeCollectionName = 'transforms';\r\n\r\n protected readonly kinds = KINDS;\r\n\r\n private readonly validation = inject(DpeValidationService);\r\n private readonly catalogs = inject(DpeFormulaCatalogStore);\r\n\r\n /**\r\n * Il nome del linguaggio di formule, dall'ambiente (ADR 0031/0036). Non e' il dialetto: il\r\n * dialetto non c'entra piu' con cio' che si scrive in quel campo, e nominarlo li' era gia'\r\n * fuorviante prima che le espressioni smettessero di essere SQL.\r\n */\r\n readonly language = computed(() => this.validation.expressionLanguage());\r\n\r\n /**\r\n * L'esito del catalogo **di questo nodo**: i simboli dipendono dallo schema a monte, e i tre\r\n * esiti del contratto si rendono diversi perche' sono diversi (`contracts.md` §7).\r\n */\r\n readonly catalogOutcome = computed(() => this.catalogs.outcomeOf(this.nodeName()));\r\n\r\n /*\r\n * `requiredForm` sta sulla base: arriva con lo **schema** del nodo, cioe' con ogni validazione, e\r\n * non con il catalogo. Cosi' c'e' anche quando il catalogo non si chiede affatto — l'ospite che\r\n * dichiara di non descrivere il proprio linguaggio — che e' il caso in cui l'incrocio con le\r\n * forme supportate serve di piu' e prima non scattava mai.\r\n */\r\n\r\n /**\r\n * L'incrocio fra la forma richiesta da **questo nodo** e le forme del compilatore **di questo\r\n * ambiente**: due sorgenti diverse, due vite diverse, ed e' l'editor che le mette insieme.\r\n * Quando non si incontrano, il campo formula non si scrive e dice perche'.\r\n */\r\n readonly formSupport = computed(() =>\r\n dpeExpressionFormSupport(this.requiredForm(), this.validation.supportedForms()),\r\n );\r\n\r\n /**\r\n * Una validazione e' in volo, quindi `requiredForm` puo' non descrivere ancora il documento che\r\n * l'utente ha davanti (ADR 0042). E' il caso che questa tendina produce: cambiare `kind` cambia\r\n * la forma richiesta **senza cambiare lo schema in uscita**, cioe' senza nessun altro segnale\r\n * che l'editor possa leggere in locale.\r\n *\r\n * Si passa il segnale nudo del servizio e non un incrocio, perche' non c'e' niente da\r\n * incrociare: e' uno stato **del documento** e **transitorio**, non un fatto su questo nodo.\r\n */\r\n readonly isFormUpdating = computed(() => this.validation.isValidating());\r\n\r\n readonly isCatalogLoading = computed(() => this.catalogs.isLoading(this.nodeName()));\r\n\r\n readonly expressionFields = computed<readonly ExpressionField[]>(\r\n () => this.node().expressionFields ?? [],\r\n );\r\n\r\n readonly orderBy = computed<readonly OrderByField[]>(() => this.node().orderBy ?? []);\r\n\r\n readonly droppedText = computed(() => (this.node().droppedFields ?? []).join(', '));\r\n readonly partitionText = computed(() => (this.node().partitionBy ?? []).join(', '));\r\n\r\n /*\r\n * I quattro campi condizionali della trasformazione, uno per voce della tabella e tutti con la\r\n * stessa forma: **la condizione vale oppure il campo porta un valore** (`frontend.md` §5-ter).\r\n * Ogni controllo sa gia' tornare all'assenza — il testo si svuota, le righe hanno il proprio ×,\r\n * e la collezione vuota vale assente come la stringa vuota.\r\n *\r\n * La finestra resta **un blocco solo** per due campi, quindi nel template la guardia e'\r\n * sull'uno **o** sull'altro: un `orderBy` rimasto su una `Expression` deve comparire anche\r\n * senza un `partitionBy` accanto.\r\n */\r\n readonly shows = dpeFieldVisibility(this.node, DPE_TRANSFORM_CONDITIONS);\r\n\r\n readonly columnNames = computed(() => this.inputColumns().map((column) => column.name));\r\n\r\n constructor() {\r\n super();\r\n /*\r\n * Il catalogo si chiede **una volta per nodo**, quando la finestra di modifica e' aperta su\r\n * questo nodo e il tipo di trasformazione governa davvero delle formule.\r\n *\r\n * Le dipendenze di questo effect sono **tre signal e nessuna altra**: nodo aperto, nome del\r\n * nodo, tipo che governa le formule. In particolare **non** legge la definizione — quella la\r\n * legge `requestFor`, in `untracked`, e leggerla qui significherebbe una richiesta di catalogo\r\n * a ogni battitura. E' il modo naturale di scriverlo e quello che `frontend.md` §5-quater\r\n * vieta: ogni richiesta costa al backend una passata completa di grafo e metadati.\r\n *\r\n * Il gesto di selezionare un nodo sul canvas non basta: l'ispettore esiste anche a finestra\r\n * chiusa (la dialog e' nel guscio e il contenuto resta montato), quindi senza la guardia\r\n * sull'apertura si pagherebbe un catalogo per ogni clic sul canvas.\r\n */\r\n effect(() => {\r\n if (this.store.isNodeEditorOpen() && this.shows.expressionFields()) {\r\n this.catalogs.requestFor(this.nodeName());\r\n }\r\n });\r\n }\r\n\r\n readonly columnHint = computed(() => {\r\n const names = this.columnNames();\r\n return names.length > 10 ? `${names.slice(0, 10).join(', ')}, …` : names.join(', ') || '—';\r\n });\r\n\r\n /**\r\n * Cambiare tipo **ripulisce** i campi che il tipo nuovo non governa. Fuori dalla propria\r\n * condizione il backend risponde `DPE_MODEL_CONDITIONAL_FORBIDDEN`, e lasciare quei valori\r\n * significa consegnare all'autore un rilievo per campo dopo un gesto con cui ha appena\r\n * dichiarato che quei campi non valgono piu'.\r\n *\r\n * Quali siano quei campi **non e' scritto qui**: lo dice `DPE_TRANSFORM_CONDITIONS`, la stessa\r\n * tabella da cui nasce `shows`. Prima erano due elenchi gemelli, e una divergenza fra i due\r\n * avrebbe prodotto un campo ripulito che l'ispettore continua a mostrare — o l'opposto — senza\r\n * che niente lo segnalasse.\r\n *\r\n * La ripulitura resta **piu' stretta di un azzeramento**: `Expression` ↔ `ComputeRelative` non\r\n * tocca `expressionFields`, perche' quella riga della tabella e' vera per entrambi i tipi.\r\n */\r\n protected onKind(value: string): void {\r\n this.patchPruned({ kind: value as TransformKind }, DPE_TRANSFORM_CONDITIONS);\r\n }\r\n\r\n protected addExpression(): void {\r\n this.patch({\r\n expressionFields: [\r\n ...this.expressionFields(),\r\n { alias: '', expression: '', declaredType: { kind: 'String' } },\r\n ],\r\n });\r\n }\r\n\r\n protected removeExpression(index: number): void {\r\n this.patch({\r\n expressionFields: this.expressionFields().filter((_, position) => position !== index),\r\n });\r\n }\r\n\r\n protected patchExpression(index: number, patch: Partial<ExpressionField>): void {\r\n this.patch({\r\n expressionFields: this.expressionFields().map((field, position) =>\r\n position === index ? { ...field, ...patch } : field,\r\n ),\r\n });\r\n }\r\n\r\n protected onDropped(raw: string): void {\r\n this.patch({ droppedFields: this.split(raw) });\r\n }\r\n\r\n protected onPartition(raw: string): void {\r\n this.patch({ partitionBy: this.split(raw) });\r\n }\r\n\r\n protected addOrder(): void {\r\n this.patch({ orderBy: [...this.orderBy(), { columnName: '' }] });\r\n }\r\n\r\n protected removeOrder(index: number): void {\r\n this.patch({ orderBy: this.orderBy().filter((_, position) => position !== index) });\r\n }\r\n\r\n protected patchOrder(index: number, patch: Partial<OrderByField>): void {\r\n this.patch({\r\n orderBy: this.orderBy().map((order, position) =>\r\n position === index ? { ...order, ...patch } : order,\r\n ),\r\n });\r\n }\r\n\r\n private split(raw: string): string[] | undefined {\r\n const values = raw\r\n .split(',')\r\n .map((value) => value.trim())\r\n .filter((value) => value.length > 0);\r\n return values.length > 0 ? values : undefined;\r\n }\r\n}\r\n","/**\r\n * Ispettore della scrittura.\r\n *\r\n * `target` decide **cosa produce la libreria**: un'istruzione SQL per l'host (`Database`) oppure\r\n * un file (`Json`, `Csv`). E' la cosa piu' importante di questo nodo, quindi sta in cima, e i\r\n * campi che seguono cambiano di conseguenza: tabella e operazione per `Database`, opzioni di\r\n * formato per il file corrispondente.\r\n *\r\n * «Di conseguenza» con un'eccezione che vale per **tutti** i campi condizionali di qui — le otto\r\n * voci di `DPE_WRITEBACK_CONDITIONS` — ed e' normativa, frontend.md §5-ter: **un campo che porta\r\n * un valore si rende anche fuori dalla propria condizione**. Vuoto e non applicabile resta\r\n * nascosto.\r\n *\r\n * Le condizioni non sono scritte qui: stanno in `core/dpe-conditional-fields.ts`, **un punto solo**\r\n * letto sia dalla resa sia dalla ripulitura di `onTarget`. Prima erano due elenchi gemelli in\r\n * questo stesso file, e divergevano gia' su `externalIdFieldName`.\r\n *\r\n * Quell'elenco e' completo di proposito, e comprende i due campi su cui il backend **non** ha\r\n * nessuna regola: `tableName` fuori da `Database` e `onlyChangedRows` su un artefatto non sono\r\n * vietati da niente. Far dipendere la resa dalla presenza di una diagnostica accoppierebbe questo\r\n * template all'elenco dei codici, cioe' gli farebbe sapere una regola di dominio; e i due campi\r\n * muti sono il caso peggiore, non quello trascurabile — nessuno li segnala e un cambio di\r\n * destinazione li puo' rendere attivi di colpo.\r\n *\r\n * Due avvisi che non sono decorazione:\r\n *\r\n * - `Insert`/`Update`/`Upsert`/`Delete` **non vengono eseguiti** dalla libreria: determinano la\r\n * forma dell'SQL di lettura consegnato all'host. Un'interfaccia che li presentasse come azioni\r\n * farebbe credere che la pipeline scriva sul database;\r\n * - `onlyChangedRows` e' **dichiarato e non implementato** da noi: il change tracking e' dell'host\r\n * (ADR 0011) e non influenza l'SQL generato.\r\n *\r\n * Della stessa specie sono `processingGroupFieldName` e `failureHandling` (ADR 0040): l'autore li\r\n * scrive, l'host li rispetta, la libreria non li esegue. `failureHandling` si puo' lasciare **non\r\n * dichiarata**, ed e' uno stato vero del documento: chi non la dichiara non deve guadagnarla\r\n * passando di qui (**I7**).\r\n */\r\n\r\nimport { ChangeDetectionStrategy, Component, computed, effect, inject } from '@angular/core';\r\n\r\nimport {\r\n DPE_WRITEBACK_CONDITIONS,\r\n dpeFieldVisibility,\r\n} from '../../core/dpe-conditional-fields';\r\nimport { DpeFormulaCatalogStore } from '../../core/dpe-formula-catalog.store';\r\nimport { dpeExpressionFormSupport } from '../../core/dpe-formula-catalog.view';\r\nimport { DpeValidationService } from '../../core/dpe-validation.service';\r\nimport {\r\n CsvDelimiter,\r\n DpeNodeCollectionName,\r\n WritebackFailureHandling,\r\n WritebackFieldMapping,\r\n WritebackFieldRole,\r\n WritebackNode,\r\n WritebackOperation,\r\n WritebackTargetKind,\r\n} from '../../model/dpe-definition.model';\r\nimport { DpeExpressionEditorComponent } from '../shared/dpe-expression-editor.component';\r\nimport { DpeSelectValueDirective } from '../shared/dpe-select-value.directive';\r\nimport { DpeNodeInspectorBase } from './dpe-node-inspector-base';\r\n\r\nconst TARGETS: readonly WritebackTargetKind[] = ['Database', 'Json', 'Csv'];\r\nconst OPERATIONS: readonly WritebackOperation[] = ['Insert', 'Update', 'Upsert', 'Delete'];\r\nconst ROLES: readonly WritebackFieldRole[] = ['Value', 'PrimaryKey', 'QualifierKey'];\r\nconst FAILURE_HANDLINGS: readonly WritebackFailureHandling[] = ['PerRecord', 'Atomic'];\r\nconst DELIMITERS: readonly CsvDelimiter[] = ['Comma', 'Semicolon', 'Tab', 'Pipe', 'Caret', 'Backquote'];\r\n\r\n@Component({\r\n selector: 'dpe-writeback-inspector',\r\n standalone: true,\r\n imports: [DpeExpressionEditorComponent, DpeSelectValueDirective],\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n template: `\r\n <div class=\"dpe-form\">\r\n <div class=\"dpe-row\">\r\n <div class=\"dpe-field\">\r\n <label class=\"dpe-field__label\" for=\"wb-target\">Destinazione</label>\r\n <select id=\"wb-target\" [dpeValue]=\"node().target\" (change)=\"onTarget(valueOf($event))\">\r\n @for (target of targets; track $index) {\r\n <option [value]=\"target\">{{ target }}</option>\r\n }\r\n </select>\r\n </div>\r\n <div class=\"dpe-field\">\r\n <label class=\"dpe-field__label\" for=\"wb-sequence\">Sequenza</label>\r\n <input\r\n id=\"wb-sequence\"\r\n type=\"number\"\r\n min=\"1\"\r\n [value]=\"node().sequence\"\r\n [attr.aria-invalid]=\"hasIssueOn('sequence')\"\r\n (change)=\"setField('sequence', toNumber(valueOf($event)) ?? 1)\"\r\n />\r\n @for (issue of issuesOn('sequence'); track $index) {\r\n <p class=\"dpe-field__issue\">{{ issue.code }} — {{ issue.message }}</p>\r\n }\r\n </div>\r\n </div>\r\n\r\n <!--\r\n Da qui in giu’ vale la regola di rendering di frontend.md §5-ter, normativa: **un campo che\r\n porta un valore si rende anche fuori dalla propria condizione**; vuoto e non applicabile\r\n resta nascosto, cosi’ il caso normale non guadagna rumore.\r\n\r\n Non e’ una questione di completezza: un valore che nessun controllo mostra non e’\r\n cancellabile dall’editor e per **I7** sopravvive a ogni salvataggio, quindi la definizione\r\n resta invalida per sempre e non riparabile da chi la sta guardando, in silenzio.\r\n\r\n Nessuna decorazione inventata qui: il controllo e’ lo stesso che si vedrebbe se la\r\n condizione valesse, e a dire perche’ non va e’ la diagnostica del backend sotto il campo.\r\n -->\r\n @if (isDatabase()) {\r\n <p class=\"dpe-note\">\r\n La libreria <strong>non scrive</strong> sul database: produce un’istruzione SQL di\r\n lettura e la consegna all’host, che la esegue. L’operazione scelta determina la forma di\r\n quell’istruzione (quali colonne, quale chiave di correlazione).\r\n </p>\r\n }\r\n\r\n @if (shows.tableName() || shows.operation()) {\r\n <div class=\"dpe-row\">\r\n @if (shows.tableName()) {\r\n <div class=\"dpe-field\">\r\n <label class=\"dpe-field__label\" for=\"wb-table\">Tabella di destinazione</label>\r\n <!--\r\n Fuori da Database il backend **non** dice niente su questo campo: nessuna regola\r\n lo vieta, quindi il valore non e’ «invalido», e’ ignorato. La regola di rendering\r\n non guarda le diagnostiche (§5-ter): un nome di tabella su un writeback di file\r\n non lo segnala nessuno, non si vede da nessuna schermata, e un cambio di\r\n destinazione lo rimette in gioco di colpo.\r\n -->\r\n <input\r\n id=\"wb-table\"\r\n type=\"text\"\r\n [value]=\"node().tableName ?? ''\"\r\n [attr.aria-invalid]=\"hasIssueOn('tableName')\"\r\n (change)=\"setField('tableName', valueOf($event))\"\r\n />\r\n @for (issue of issuesOn('tableName'); track $index) {\r\n <p class=\"dpe-field__issue\">{{ issue.code }} — {{ issue.message }}</p>\r\n }\r\n </div>\r\n }\r\n @if (shows.operation()) {\r\n <div class=\"dpe-field\">\r\n <label class=\"dpe-field__label\" for=\"wb-operation\">Operazione</label>\r\n <!--\r\n La voce vuota c’e’ **sempre**, non solo fuori condizione: «operazione assente» e’\r\n uno stato vero del documento — l’unico ammesso su un target di file — e senza di\r\n essa il valore scritto fuori posto non si potrebbe togliere, che e’ il difetto che\r\n questa regola esiste per chiudere. Su Database l’assenza e’ un errore, e il backend\r\n lo dice qui sotto: dirlo e’ suo, non nostro. Toglie anche una bugia vecchia, la\r\n tendina che mostrava «Insert» su un documento che non dichiarava niente.\r\n -->\r\n <select\r\n id=\"wb-operation\"\r\n [dpeValue]=\"node().operation ?? ''\"\r\n [attr.aria-invalid]=\"hasIssueOn('operation')\"\r\n (change)=\"setField('operation', valueOf($event))\"\r\n >\r\n <option value=\"\">Non dichiarata</option>\r\n @for (operation of operations; track $index) {\r\n <option [value]=\"operation\">{{ operation }}</option>\r\n }\r\n </select>\r\n @for (issue of issuesOn('operation'); track $index) {\r\n <p class=\"dpe-field__issue\">{{ issue.code }} — {{ issue.message }}</p>\r\n }\r\n </div>\r\n }\r\n </div>\r\n }\r\n\r\n @if (shows.externalIdFieldName()) {\r\n <div class=\"dpe-field\">\r\n <label class=\"dpe-field__label\" for=\"wb-external-id\">Campo di identificazione esterna</label>\r\n <input\r\n id=\"wb-external-id\"\r\n type=\"text\"\r\n [value]=\"node().externalIdFieldName ?? ''\"\r\n [attr.aria-invalid]=\"hasIssueOn('externalIdFieldName')\"\r\n (change)=\"setField('externalIdFieldName', valueOf($event))\"\r\n />\r\n @for (issue of issuesOn('externalIdFieldName'); track $index) {\r\n <p class=\"dpe-field__issue\">{{ issue.code }} — {{ issue.message }}</p>\r\n }\r\n </div>\r\n }\r\n\r\n @if (shows.onlyChangedRows()) {\r\n <!--\r\n Una casella **sa** dire «assente», a patto che spenta tolga la chiave invece di\r\n scrivere «falso»: senza quello il controllo comparirebbe fuori condizione senza poter\r\n togliere niente. Anche qui il backend tace — non c’e’ nessuna regola su questo campo\r\n fuori da Database — e la regola di rendering non lo chiede.\r\n -->\r\n <label class=\"dpe-check\">\r\n <input\r\n id=\"wb-only-changed\"\r\n type=\"checkbox\"\r\n [checked]=\"node().onlyChangedRows ?? false\"\r\n (change)=\"setFlag('onlyChangedRows', checkedOf($event))\"\r\n />\r\n Solo le righe cambiate\r\n </label>\r\n <p class=\"dpe-field__hint\">\r\n Dichiarazione per l’host: il confronto con lo stato precedente non lo fa la libreria e\r\n non cambia l’SQL generato.\r\n </p>\r\n }\r\n\r\n @if (shows.processingGroupFieldName() || shows.failureHandling()) {\r\n <div class=\"dpe-row\">\r\n @if (shows.processingGroupFieldName()) {\r\n <div class=\"dpe-field\">\r\n <label class=\"dpe-field__label\" for=\"wb-group-field\">Campo di raggruppamento</label>\r\n <!--\r\n Testo con datalist e non una tendina: il valore e' un nome di campo di destinazione\r\n authored, che puo' riferirsi a una mappatura non ancora scritta o appena rinominata.\r\n Una tendina che non trovasse l'opzione mostrerebbe la prima voce al posto del valore\r\n vero, che e' il difetto per cui esiste la direttiva dpeValue — qui si evita invece\r\n di ripararlo.\r\n -->\r\n <input\r\n id=\"wb-group-field\"\r\n type=\"text\"\r\n list=\"wb-target-fields\"\r\n [value]=\"node().processingGroupFieldName ?? ''\"\r\n [attr.aria-invalid]=\"hasIssueOn('processingGroupFieldName')\"\r\n (change)=\"setField('processingGroupFieldName', valueOf($event))\"\r\n />\r\n @for (issue of issuesOn('processingGroupFieldName'); track $index) {\r\n <p class=\"dpe-field__issue\">{{ issue.code }} — {{ issue.message }}</p>\r\n }\r\n </div>\r\n }\r\n @if (shows.failureHandling()) {\r\n <div class=\"dpe-field\">\r\n <label class=\"dpe-field__label\" for=\"wb-failure-handling\">Gestione del fallimento</label>\r\n <!--\r\n L'opzione vuota non e' un segnaposto: «non dichiarata» e' uno stato del documento,\r\n distinto da entrambe le dichiarazioni, e deve restare scegliibile per tornare\r\n indietro. Sceglierla toglie la chiave dal documento invece di scriverci una stringa\r\n vuota, quindi un salva-dopo-carica rispedisce il documento identico (I7). Ed e'\r\n anche il solo modo di togliere il valore quando il campo si rende fuori condizione.\r\n -->\r\n <select\r\n id=\"wb-failure-handling\"\r\n [dpeValue]=\"node().failureHandling ?? ''\"\r\n [attr.aria-invalid]=\"hasIssueOn('failureHandling')\"\r\n (change)=\"setField('failureHandling', valueOf($event))\"\r\n >\r\n <option value=\"\">Non dichiarata</option>\r\n @for (handling of failureHandlings; track $index) {\r\n <option [value]=\"handling\">{{ handling }}</option>\r\n }\r\n </select>\r\n @for (issue of issuesOn('failureHandling'); track $index) {\r\n <p class=\"dpe-field__issue\">{{ issue.code }} — {{ issue.message }}</p>\r\n }\r\n </div>\r\n }\r\n </div>\r\n <p class=\"dpe-field__hint\">\r\n Due dichiarazioni per l’host, senza effetto sull’SQL: il campo di raggruppamento dice con\r\n quale colonna spezzare il lavoro in lotti, <code>Atomic</code> obbliga a scrivere tutto o\r\n niente e <code>PerRecord</code> consente il successo parziale. Lasciata non dichiarata,\r\n la regola effettiva la stabilisce il backend — un writeback dentro un gruppo atomico\r\n resta atomico comunque.\r\n </p>\r\n <!--\r\n Lo Step porta la regola effettiva **solo** dove l’host scrive davvero: su un artefatto il\r\n campo e’ assente di proposito, e su un artefatto questa riga tace da se’ — la guardia e’\r\n sul valore, non sul ramo, quindi regge anche ora che i due campi si rendono fuori dal\r\n ramo Database. Tace anche prima della prima risposta del backend, che e’ meglio di una\r\n regola indovinata in locale.\r\n -->\r\n @if (step()?.failureHandling; as regola) {\r\n <p class=\"dpe-field__hint\">\r\n Regola effettiva secondo il backend: <strong>{{ regola }}</strong\r\n >@if (step()?.atomicGroupName; as gruppo) {\r\n , imposta dal gruppo atomico <code>{{ gruppo }}</code\r\n >}.\r\n </p>\r\n }\r\n <datalist id=\"wb-target-fields\">\r\n @for (field of fields(); track $index) {\r\n <option [value]=\"field.targetFieldName\"></option>\r\n }\r\n </datalist>\r\n }\r\n\r\n @if (shows.csvOptions()) {\r\n <div class=\"dpe-row\">\r\n <div class=\"dpe-field\">\r\n <label class=\"dpe-field__label\" for=\"wb-delimiter\">Separatore</label>\r\n <select\r\n id=\"wb-delimiter\"\r\n [dpeValue]=\"node().csvOptions?.delimiter ?? 'Comma'\"\r\n (change)=\"patchCsv({ delimiter: asDelimiter(valueOf($event)) })\"\r\n >\r\n @for (delimiter of delimiters; track $index) {\r\n <option [value]=\"delimiter\">{{ delimiter }}</option>\r\n }\r\n </select>\r\n </div>\r\n <label class=\"dpe-check\">\r\n <input\r\n type=\"checkbox\"\r\n [checked]=\"node().csvOptions?.includesHeaderRow ?? true\"\r\n (change)=\"patchCsv({ includesHeaderRow: checkedOf($event) })\"\r\n />\r\n Riga di intestazione\r\n </label>\r\n <!--\r\n Il comando c’e’ quando **l’oggetto c’e’**, dentro e fuori dalla propria condizione: le\r\n opzioni sono facoltative anche sul target che le ammette (assenti valgono i default), e\r\n senza questo comando un csvOptions scritto su un writeback di database non sarebbe\r\n togliibile — ne’ la tendina ne’ la casella sanno dire «assente», sanno dire solo un\r\n altro valore. E’ la stessa ragione per cui la tendina del fallimento ha la voce vuota.\r\n -->\r\n @if (node().csvOptions) {\r\n <button type=\"button\" class=\"dpe-btn\" (click)=\"removeCsvOptions()\">\r\n Rimuovi le opzioni CSV\r\n </button>\r\n }\r\n </div>\r\n @for (issue of issuesOn('csvOptions'); track $index) {\r\n <p class=\"dpe-field__issue\">{{ issue.code }} — {{ issue.message }}</p>\r\n }\r\n <p class=\"dpe-field__hint\">\r\n Il quoting non e’ configurabile: si quota quando serve, con le virgolette raddoppiate.\r\n </p>\r\n }\r\n\r\n @if (shows.jsonOptions()) {\r\n <div class=\"dpe-row\">\r\n <label class=\"dpe-check\">\r\n <input\r\n type=\"checkbox\"\r\n [checked]=\"node().jsonOptions?.indented ?? false\"\r\n (change)=\"patchJson(checkedOf($event))\"\r\n />\r\n JSON indentato\r\n </label>\r\n @if (node().jsonOptions) {\r\n <button type=\"button\" class=\"dpe-btn\" (click)=\"removeJsonOptions()\">\r\n Rimuovi le opzioni JSON\r\n </button>\r\n }\r\n </div>\r\n @for (issue of issuesOn('jsonOptions'); track $index) {\r\n <p class=\"dpe-field__issue\">{{ issue.code }} — {{ issue.message }}</p>\r\n }\r\n }\r\n\r\n <!--\r\n Il **terzo** sito di authoring di formule (ADR 0031 §1), quello che si dimentica: non e’ un\r\n valore ma un **predicato**, e passa dal compilatore come le colonne calcolate.\r\n -->\r\n <dpe-expression-editor\r\n label=\"Filtro sul risultato (facoltativo)\"\r\n [value]=\"node().filterCondition\"\r\n [language]=\"language()\"\r\n [columns]=\"columnNames()\"\r\n [issues]=\"issuesOn('filterCondition')\"\r\n [outcome]=\"catalogOutcome()\"\r\n [requiredForm]=\"requiredForm()\"\r\n [formSupport]=\"formSupport()\"\r\n [isFormUpdating]=\"isFormUpdating()\"\r\n [isCatalogLoading]=\"isCatalogLoading()\"\r\n controlId=\"wb-filter\"\r\n (valueChange)=\"setField('filterCondition', $event)\"\r\n />\r\n\r\n <section class=\"dpe-section\">\r\n <h4 class=\"dpe-section__title\">Mappatura dei campi</h4>\r\n @if (fields().length === 0) {\r\n <p class=\"dpe-empty\">Nessun campo mappato.</p>\r\n } @else {\r\n <table class=\"dpe-table\">\r\n <thead>\r\n <tr>\r\n <th>Colonna a monte</th>\r\n <th>Parametro</th>\r\n <th>Campo di destinazione</th>\r\n <th>Ruolo</th>\r\n <th>Auto</th>\r\n <th></th>\r\n </tr>\r\n </thead>\r\n <tbody>\r\n @for (field of fields(); track $index) {\r\n <tr>\r\n <td>\r\n <!-- Colonna e parametro sono esclusivi: scegliere l’uno svuota l’altro. -->\r\n <input\r\n type=\"text\"\r\n list=\"wb-columns\"\r\n [value]=\"field.sourceFieldName ?? ''\"\r\n [disabled]=\"!!field.runtimeParameterName || (field.isAutoGenerated ?? false)\"\r\n [attr.aria-label]=\"'Colonna del campo ' + ($index + 1)\"\r\n (change)=\"patchField($index, { sourceFieldName: valueOf($event) || undefined })\"\r\n />\r\n </td>\r\n <td>\r\n <select\r\n [dpeValue]=\"field.runtimeParameterName ?? ''\"\r\n [attr.aria-label]=\"'Parametro del campo ' + ($index + 1)\"\r\n (change)=\"onRuntimeParameter($index, valueOf($event))\"\r\n >\r\n <option value=\"\">—</option>\r\n @for (parameter of valueParameters(); track $index) {\r\n <option [value]=\"parameter.name\">{{ parameter.name }}</option>\r\n }\r\n </select>\r\n </td>\r\n <td>\r\n <input\r\n type=\"text\"\r\n [value]=\"field.targetFieldName\"\r\n [attr.aria-label]=\"'Campo di destinazione ' + ($index + 1)\"\r\n (change)=\"patchField($index, { targetFieldName: valueOf($event) })\"\r\n />\r\n </td>\r\n <td>\r\n <select\r\n [dpeValue]=\"field.role ?? 'Value'\"\r\n [attr.aria-label]=\"'Ruolo del campo ' + ($index + 1)\"\r\n (change)=\"patchField($index, { role: asRole(valueOf($event)) })\"\r\n >\r\n @for (role of roles; track $index) {\r\n <option [value]=\"role\">{{ role }}</option>\r\n }\r\n </select>\r\n </td>\r\n <td>\r\n <input\r\n type=\"checkbox\"\r\n [checked]=\"field.isAutoGenerated ?? false\"\r\n [attr.aria-label]=\"'Generato dalla destinazione ' + ($index + 1)\"\r\n (change)=\"patchField($index, { isAutoGenerated: checkedOf($event) })\"\r\n />\r\n </td>\r\n <td>\r\n <button\r\n type=\"button\"\r\n class=\"dpe-btn dpe-btn--icon dpe-btn--danger\"\r\n aria-label=\"Rimuovi la mappatura\"\r\n (click)=\"removeField($index)\"\r\n >\r\n ×\r\n </button>\r\n </td>\r\n </tr>\r\n }\r\n </tbody>\r\n </table>\r\n }\r\n <datalist id=\"wb-columns\">\r\n @for (column of inputColumns(); track $index) {\r\n <option [value]=\"column.name\">{{ column.type.kind }}</option>\r\n }\r\n </datalist>\r\n <div>\r\n <button type=\"button\" class=\"dpe-btn\" (click)=\"addField()\">Aggiungi mappatura</button>\r\n </div>\r\n </section>\r\n </div>\r\n `,\r\n})\r\nexport class DpeWritebackInspectorComponent extends DpeNodeInspectorBase<WritebackNode> {\r\n override readonly collection: DpeNodeCollectionName = 'writebacks';\r\n\r\n protected readonly targets = TARGETS;\r\n protected readonly operations = OPERATIONS;\r\n protected readonly roles = ROLES;\r\n protected readonly delimiters = DELIMITERS;\r\n protected readonly failureHandlings = FAILURE_HANDLINGS;\r\n\r\n private readonly validation = inject(DpeValidationService);\r\n private readonly catalogs = inject(DpeFormulaCatalogStore);\r\n\r\n /** Il nome del linguaggio di formule, dall'ambiente (ADR 0031/0036). Non e' il dialetto. */\r\n readonly language = computed(() => this.validation.expressionLanguage());\r\n\r\n /**\r\n * L'esito del catalogo **di questo nodo**: i simboli dipendono dallo schema a monte, e i tre\r\n * esiti del contratto si rendono diversi perche' sono diversi (`contracts.md` §7).\r\n */\r\n readonly catalogOutcome = computed(() => this.catalogs.outcomeOf(this.nodeName()));\r\n\r\n /*\r\n * `requiredForm` sta sulla base: arriva con lo **schema** del nodo, cioe' con ogni validazione, e\r\n * non con il catalogo — che per un writeback puo' benissimo non essere mai chiesto.\r\n */\r\n\r\n /**\r\n * L'incrocio fra la forma richiesta da **questo nodo** — qui e' sempre un predicato — e le forme\r\n * che il compilatore di **questo ambiente** sa produrre. Se il predicato non e' fra quelle, il\r\n * filtro sul risultato non si scrive e il campo dice perche'.\r\n */\r\n readonly formSupport = computed(() =>\r\n dpeExpressionFormSupport(this.requiredForm(), this.validation.supportedForms()),\r\n );\r\n\r\n /**\r\n * Come sulla trasformazione: mentre una validazione e' in volo la pastiglia della forma non si\r\n * afferma (ADR 0042). Qui la forma non cambia mai — un writeback pretende sempre un predicato —\r\n * ma il segnale si passa **lo stesso**: che oggi sia costante e' una proprieta' del backend, non\r\n * di questo componente, e sceglierne l'assenza in base a quella proprieta' significherebbe\r\n * riscrivere qui la regola che decide la forma.\r\n */\r\n readonly isFormUpdating = computed(() => this.validation.isValidating());\r\n\r\n readonly isCatalogLoading = computed(() => this.catalogs.isLoading(this.nodeName()));\r\n\r\n readonly isDatabase = computed(() => this.node().target === 'Database');\r\n\r\n constructor() {\r\n super();\r\n /*\r\n * Una volta per nodo, e solo a finestra aperta: l'ispettore resta montato anche quando la\r\n * dialog e' chiusa, quindi senza la guardia ogni clic di selezione sul canvas costerebbe al\r\n * backend una passata completa di grafo e metadati (`frontend.md` §5-quater).\r\n *\r\n * `requestFor` legge la definizione in `untracked`: leggerla qui renderebbe questo effect\r\n * dipendente dal documento, cioe' una richiesta a ogni battitura.\r\n */\r\n effect(() => {\r\n if (this.store.isNodeEditorOpen()) {\r\n this.catalogs.requestFor(this.nodeName());\r\n }\r\n });\r\n }\r\n\r\n /*\r\n * Gli otto campi condizionali di questo nodo, uno per voce di `DPE_WRITEBACK_CONDITIONS` e tutti\r\n * con la stessa forma: **la condizione vale oppure il campo porta un valore** (frontend.md\r\n * §5-ter).\r\n *\r\n * Stanno qui e non nel template perche' la condizione e' una sola idea detta otto volte: nel\r\n * template sarebbero otto espressioni composte da rileggere una per una, e la nona scritta fra\r\n * un anno si dimenticherebbe la meta' «oppure porta un valore» — che e' esattamente il modo in\r\n * cui questo difetto e' nato.\r\n *\r\n * La condizione **non** e' la regola di dominio: dire che il valore e' sbagliato tocca al\r\n * backend, e lo dice con `DPE_MODEL_CONDITIONAL_FORBIDDEN` sotto il campo. Qui si decide solo\r\n * se un controllo esiste sullo schermo, e per questo la meta' che conta guarda il documento, non\r\n * la validita'.\r\n */\r\n readonly shows = dpeFieldVisibility(this.node, DPE_WRITEBACK_CONDITIONS);\r\n\r\n /**\r\n * Lo `Step` di questo writeback, come il backend lo ha consegnato con la validazione.\r\n *\r\n * Serve a una cosa sola e importante: mostrare la gestione del fallimento **efficace**. Quel\r\n * valore e' il risultato di tre regole — appartenenza a un gruppo atomico, valore dichiarato,\r\n * default — e ricostruirle qui sarebbe reimplementare dominio nell'editor. Finche' la\r\n * validazione non e' tornata non c'e' nessuno `Step` e non si mostra niente, che e' meglio di\r\n * un valore indovinato in locale.\r\n */\r\n readonly step = computed(() =>\r\n this.store.writebackSequence().find((entry) => entry.writebackName === this.nodeName()),\r\n );\r\n\r\n readonly fields = computed<readonly WritebackFieldMapping[]>(() => this.node().fields ?? []);\r\n\r\n readonly columnNames = computed(() => this.inputColumns().map((column) => column.name));\r\n\r\n readonly valueParameters = computed(() =>\r\n this.parameters().filter((parameter) => parameter.role === 'Value'),\r\n );\r\n\r\n protected asRole(value: string): WritebackFieldRole {\r\n return value as WritebackFieldRole;\r\n }\r\n\r\n protected asDelimiter(value: string): CsvDelimiter {\r\n return value as CsvDelimiter;\r\n }\r\n\r\n /**\r\n * Cambiare destinazione **toglie** i campi che la destinazione nuova non governa: `operation` e'\r\n * richiesta per `Database` e deve essere assente per `Json`/`Csv`, quindi lasciarla scritta\r\n * produrrebbe `DPE_MODEL_CONDITIONAL_FORBIDDEN`.\r\n *\r\n * La ragione che questo metodo aveva prima — «su un campo che l'utente non vede piu'» — **non\r\n * vale piu'** da §5-ter: adesso quei valori resterebbero visibili e togliibili a mano. Quella\r\n * che resta, e che l'architetto ha confermato: cambiare destinazione e' un gesto **deliberato**\r\n * con cui l'autore dichiara un'altra forma del nodo, e ripulire esegue quell'intento invece di\r\n * regalargli un rilievo per campo da chiudere uno per uno.\r\n *\r\n * **Quali** campi escano non e' scritto qui: lo dice `DPE_WRITEBACK_CONDITIONS`, la stessa\r\n * tabella da cui nasce `shows`. `onlyChangedRows` e' dentro come gli altri — il backend su quel\r\n * campo non ha nessuna regola e la sua casella restava accesa su un artefatto senza che nessuno\r\n * la segnalasse — e **`externalIdFieldName` segue `operation`, non `target`**: e' la condizione\r\n * del backend (`Operation == Upsert`), ed e' il punto su cui i due elenchi gemelli di prima\r\n * divergevano gia'.\r\n *\r\n * L'unica cosa che resta scritta qui e' il **valore iniziale** dell'operazione, che non e' una\r\n * condizione: scegliere `Database` dichiara che un'operazione ci vuole, e la tendina senza\r\n * default direbbe «Non dichiarata» su un nodo appena portato li'. Si semina solo se manca,\r\n * cosi' un `Upsert` scritto altrove sopravvive al passaggio — e con esso il suo campo di\r\n * identificazione esterna.\r\n *\r\n * **Ripulire e' togliere la chiave**, non scrivere `false`: e' la stessa disciplina di\r\n * `setFlag()`, e senza di essa un `onlyChangedRows: false` resterebbe nel documento a nome di\r\n * nessuno (**I7**).\r\n *\r\n * Cio' che rende la scelta non distruttiva e' che questa ripulitura passa da `patch`, quindi da\r\n * `DpeDocumentStore.updateNode` → `commit`, che mette l'istantanea **precedente** nella\r\n * cronologia: «Annulla» rimette i valori. Verificato a schermo, non dedotto. Se non fosse cosi'\r\n * sarebbe una perdita di dati irreversibile su un gesto di routine, e il metodo non dovrebbe\r\n * esistere.\r\n */\r\n protected onTarget(value: string): void {\r\n const target = value as WritebackTargetKind;\r\n const change: Partial<WritebackNode> =\r\n target === 'Database' ? { target, operation: this.node().operation ?? 'Insert' } : { target };\r\n this.patchPruned(change, DPE_WRITEBACK_CONDITIONS);\r\n }\r\n\r\n // Lo spread non esiste nelle espressioni dei template Angular: la fusione sta qui.\r\n protected patchJson(indented: boolean): void {\r\n this.patch({ jsonOptions: { ...(this.node().jsonOptions ?? {}), indented } });\r\n }\r\n\r\n /*\r\n * Togliere l'oggetto intero, non svuotarne i campi: `{}` e' un `jsonOptions` dichiarato, e per\r\n * il backend dichiararlo fuori dal proprio target e' `DPE_MODEL_CONDITIONAL_FORBIDDEN` qualunque\r\n * cosa contenga.\r\n */\r\n protected removeJsonOptions(): void {\r\n this.patch({ jsonOptions: undefined });\r\n }\r\n\r\n protected removeCsvOptions(): void {\r\n this.patch({ csvOptions: undefined });\r\n }\r\n\r\n protected patchCsv(patch: Partial<{ delimiter: CsvDelimiter; includesHeaderRow: boolean }>): void {\r\n this.patch({ csvOptions: { ...(this.node().csvOptions ?? {}), ...patch } });\r\n }\r\n\r\n protected addField(): void {\r\n this.patch({ fields: [...this.fields(), { targetFieldName: '' }] });\r\n }\r\n\r\n protected removeField(index: number): void {\r\n this.patch({ fields: this.fields().filter((_, position) => position !== index) });\r\n }\r\n\r\n protected patchField(index: number, patch: Partial<WritebackFieldMapping>): void {\r\n this.patch({\r\n fields: this.fields().map((field, position) =>\r\n position === index ? { ...field, ...patch } : field,\r\n ),\r\n });\r\n }\r\n\r\n protected onRuntimeParameter(index: number, name: string): void {\r\n this.patchField(index, {\r\n runtimeParameterName: name === '' ? undefined : name,\r\n sourceFieldName: name === '' ? this.fields()[index]?.sourceFieldName : undefined,\r\n });\r\n }\r\n}\r\n","/**\r\n * **Solo** i campi modificabili di **un** nodo: i campi comuni, poi l'ispettore della categoria.\r\n *\r\n * Cio' che non c'e' piu', e dove e' andato (ADR 0026): la testata di identita' e' della dialog che\r\n * lo contiene, le due sezioni di fatti in sola lettura sono di `dpe-node-facts`, e il caso «piu' di\r\n * un nodo selezionato» e' una striscia contestuale del guscio. Non c'e' **nessun** booleano di\r\n * modalita' al loro posto: si e' spostato contenuto, non aggiunto un interruttore che cambia il\r\n * significato del componente.\r\n *\r\n * La **rinomina** sta qui e non nei singoli ispettori, per due ragioni. E' identica per tutte le\r\n * categorie, e soprattutto e' un refactoring globale (**I5**): passa dall'unico punto che\r\n * riscrive i riferimenti a valle, e prima di applicarla **dice quante occorrenze tocchera'**.\r\n * Rinominare non e' modificare un campo, e non deve sembrarlo. E' anche l'unica eccezione al\r\n * commit dal vivo: ha una bozza locale perche' ha un bottone, non perche' sia in una dialog.\r\n */\r\n\r\nimport {\r\n ChangeDetectionStrategy,\r\n Component,\r\n computed,\r\n effect,\r\n inject,\r\n signal,\r\n} from '@angular/core';\r\n\r\nimport { DpeDocumentStore } from '../../core/dpe-document.store';\r\nimport { dpeFindNode } from '../../core/dpe-graph';\r\nimport {\r\n AggregateNode,\r\n AppendNode,\r\n AtomicWritebackNode,\r\n CustomNode,\r\n DataSourceNode,\r\n FilterNode,\r\n ForecastNode,\r\n HierarchyPathNode,\r\n JoinNode,\r\n TransformNode,\r\n WritebackNode,\r\n} from '../../model/dpe-definition.model';\r\nimport { DpeAggregateInspectorComponent } from './dpe-aggregate-inspector.component';\r\nimport { DpeAppendInspectorComponent } from './dpe-append-inspector.component';\r\nimport { DpeAtomicWritebackInspectorComponent } from './dpe-atomic-writeback-inspector.component';\r\nimport { DpeCustomNodeInspectorComponent } from './dpe-custom-node-inspector.component';\r\nimport { DpeDataSourceInspectorComponent } from './dpe-data-source-inspector.component';\r\nimport { DpeFilterInspectorComponent } from './dpe-filter-inspector.component';\r\nimport { DpeForecastInspectorComponent } from './dpe-forecast-inspector.component';\r\nimport { DpeHierarchyPathInspectorComponent } from './dpe-hierarchy-path-inspector.component';\r\nimport { DpeJoinInspectorComponent } from './dpe-join-inspector.component';\r\nimport { DpeTransformInspectorComponent } from './dpe-transform-inspector.component';\r\nimport { DpeWritebackInspectorComponent } from './dpe-writeback-inspector.component';\r\n\r\n@Component({\r\n selector: 'dpe-node-inspector',\r\n standalone: true,\r\n imports: [\r\n DpeAggregateInspectorComponent,\r\n DpeAppendInspectorComponent,\r\n DpeAtomicWritebackInspectorComponent,\r\n DpeCustomNodeInspectorComponent,\r\n DpeDataSourceInspectorComponent,\r\n DpeFilterInspectorComponent,\r\n DpeForecastInspectorComponent,\r\n DpeHierarchyPathInspectorComponent,\r\n DpeJoinInspectorComponent,\r\n DpeTransformInspectorComponent,\r\n DpeWritebackInspectorComponent,\r\n ],\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n template: `\r\n @if (located(); as entry) {\r\n <div class=\"dpe-form\">\r\n <div class=\"dpe-field\">\r\n <label class=\"dpe-field__label\" for=\"node-name\">Nome tecnico</label>\r\n <div class=\"dpe-row\">\r\n <input\r\n id=\"node-name\"\r\n type=\"text\"\r\n [value]=\"nameDraft()\"\r\n [attr.aria-invalid]=\"nameDraft().trim().length === 0\"\r\n (input)=\"nameDraft.set(valueOf($event))\"\r\n />\r\n <button\r\n type=\"button\"\r\n class=\"dpe-btn\"\r\n [disabled]=\"!canRename()\"\r\n (click)=\"rename(entry.node.name)\"\r\n >\r\n Rinomina\r\n </button>\r\n </div>\r\n @if (canRename()) {\r\n <p class=\"dpe-note dpe-note--warning\">\r\n La rinomina aggiorna <strong>{{ referenceCount() }}</strong> riferimenti a valle.\r\n I nomi si confrontano in modo esatto: un riferimento che sbaglia le maiuscole non\r\n verrebbe risolto dal backend.\r\n </p>\r\n }\r\n </div>\r\n\r\n <div class=\"dpe-field\">\r\n <label class=\"dpe-field__label\" for=\"node-label\">Etichetta</label>\r\n <input\r\n id=\"node-label\"\r\n type=\"text\"\r\n [value]=\"entry.node.label ?? ''\"\r\n (change)=\"setCommon('label', valueOf($event))\"\r\n />\r\n </div>\r\n\r\n <div class=\"dpe-field\">\r\n <label class=\"dpe-field__label\" for=\"node-description\">Descrizione</label>\r\n <input\r\n id=\"node-description\"\r\n type=\"text\"\r\n [value]=\"entry.node.description ?? ''\"\r\n (change)=\"setCommon('description', valueOf($event))\"\r\n />\r\n </div>\r\n </div>\r\n\r\n <section class=\"dpe-section\">\r\n @switch (entry.collection) {\r\n @case ('dataSources') {\r\n <dpe-data-source-inspector [nodeName]=\"entry.node.name\" [node]=\"asDataSource(entry.node)\" />\r\n }\r\n @case ('filters') {\r\n <dpe-filter-inspector [nodeName]=\"entry.node.name\" [node]=\"asFilter(entry.node)\" />\r\n }\r\n @case ('joins') {\r\n <dpe-join-inspector [nodeName]=\"entry.node.name\" [node]=\"asJoin(entry.node)\" />\r\n }\r\n @case ('aggregates') {\r\n <dpe-aggregate-inspector [nodeName]=\"entry.node.name\" [node]=\"asAggregate(entry.node)\" />\r\n }\r\n @case ('transforms') {\r\n <dpe-transform-inspector [nodeName]=\"entry.node.name\" [node]=\"asTransform(entry.node)\" />\r\n }\r\n @case ('appends') {\r\n <dpe-append-inspector [nodeName]=\"entry.node.name\" [node]=\"asAppend(entry.node)\" />\r\n }\r\n @case ('hierarchyPaths') {\r\n <dpe-hierarchy-path-inspector [nodeName]=\"entry.node.name\" [node]=\"asHierarchy(entry.node)\" />\r\n }\r\n @case ('forecasts') {\r\n <dpe-forecast-inspector [nodeName]=\"entry.node.name\" [node]=\"asForecast(entry.node)\" />\r\n }\r\n @case ('customNodes') {\r\n <dpe-custom-node-inspector [nodeName]=\"entry.node.name\" [node]=\"asCustom(entry.node)\" />\r\n }\r\n @case ('writebacks') {\r\n <dpe-writeback-inspector [nodeName]=\"entry.node.name\" [node]=\"asWriteback(entry.node)\" />\r\n }\r\n @case ('atomicWritebacks') {\r\n <dpe-atomic-writeback-inspector [nodeName]=\"entry.node.name\" [node]=\"asAtomic(entry.node)\" />\r\n }\r\n }\r\n </section>\r\n } @else {\r\n <p class=\"dpe-empty\">Nessun nodo selezionato.</p>\r\n }\r\n `,\r\n})\r\nexport class DpeNodeInspectorComponent {\r\n private readonly store = inject(DpeDocumentStore);\r\n\r\n readonly located = computed(() => {\r\n const name = this.store.selectedNodeName();\r\n return name === undefined ? undefined : dpeFindNode(this.store.definition(), name);\r\n });\r\n\r\n readonly nameDraft = signal('');\r\n\r\n readonly referenceCount = computed(() => {\r\n const entry = this.located();\r\n return entry ? this.store.countReferences(entry.node.name) : 0;\r\n });\r\n\r\n readonly canRename = computed(() => {\r\n const entry = this.located();\r\n const draft = this.nameDraft().trim();\r\n return !!entry && draft.length > 0 && draft !== entry.node.name;\r\n });\r\n\r\n constructor() {\r\n // La bozza del nome segue la selezione: senza, cambiando nodo si resterebbe con il nome\r\n // del precedente in una casella che rinomina.\r\n effect(() => {\r\n const entry = this.located();\r\n this.nameDraft.set(entry?.node.name ?? '');\r\n });\r\n }\r\n\r\n protected rename(currentName: string): void {\r\n this.store.renameNode(currentName, this.nameDraft().trim());\r\n }\r\n\r\n protected setCommon(field: 'label' | 'description', value: string): void {\r\n const entry = this.located();\r\n if (!entry) {\r\n return;\r\n }\r\n this.store.updateNode(entry.collection, entry.node.name, {\r\n [field]: value === '' ? undefined : value,\r\n } as never);\r\n }\r\n\r\n protected valueOf(event: Event): string {\r\n return (event.target as HTMLInputElement).value;\r\n }\r\n\r\n // I cast sono confinati qui: `@switch` restringe la collezione ma non il tipo del nodo, che\r\n // nel modello e' un'unione. Un cast per ramo e' il prezzo, e sbagliarne uno e' un errore di\r\n // compilazione nel template dell'ispettore, non un difetto silenzioso.\r\n protected asDataSource(node: unknown): DataSourceNode {\r\n return node as DataSourceNode;\r\n }\r\n\r\n protected asFilter(node: unknown): FilterNode {\r\n return node as FilterNode;\r\n }\r\n\r\n protected asJoin(node: unknown): JoinNode {\r\n return node as JoinNode;\r\n }\r\n\r\n protected asAggregate(node: unknown): AggregateNode {\r\n return node as AggregateNode;\r\n }\r\n\r\n protected asTransform(node: unknown): TransformNode {\r\n return node as TransformNode;\r\n }\r\n\r\n protected asAppend(node: unknown): AppendNode {\r\n return node as AppendNode;\r\n }\r\n\r\n protected asHierarchy(node: unknown): HierarchyPathNode {\r\n return node as HierarchyPathNode;\r\n }\r\n\r\n protected asForecast(node: unknown): ForecastNode {\r\n return node as ForecastNode;\r\n }\r\n\r\n protected asCustom(node: unknown): CustomNode {\r\n return node as CustomNode;\r\n }\r\n\r\n protected asWriteback(node: unknown): WritebackNode {\r\n return node as WritebackNode;\r\n }\r\n\r\n protected asAtomic(node: unknown): AtomicWritebackNode {\r\n return node as AtomicWritebackNode;\r\n }\r\n}\r\n","/**\r\n * La finestra di modifica di un nodo: elemento `<dialog>` **nativo**, aperto con `showModal()`\r\n * (ADR 0026).\r\n *\r\n * Perche' l'elemento nativo e non un overlay scritto a mano: **Esc**, il **focus trap** (con la\r\n * finestra aperta il Tab non esce), il **fondo inerte** per puntatore *e* tastiera, `::backdrop`\r\n * come pseudo-elemento standard e nessun `z-index` — il top layer sta sopra ogni stacking context\r\n * senza partecipare alla gara. Sono tutte cose che un overlay dovrebbe reimplementare, e il focus\r\n * trap in particolare non si scrive in due righe.\r\n *\r\n * **Una sola istanza** nel guscio, fratello di `.dpe-shell__body`. Resta nel sottoalbero DOM del\r\n * componente e non si trapianta in `document.body`: il top layer e' una promozione **visiva**, non\r\n * un trapianto, quindi gli eventi risalgono l'albero e il `Ctrl+S` del guscio continua a\r\n * funzionare.\r\n *\r\n * **Le modifiche si applicano dal vivo**: gli ispettori scrivono nello store a ogni `(change)`,\r\n * qui non esiste nessuna bozza locale e il piede ha «Chiudi», non «OK / Annulla». Una bozza\r\n * produrrebbe due nozioni di annullamento in disaccordo con ↶ e rilievi che descrivono un\r\n * documento diverso da quello mostrato. Conseguenza dichiarata in interfaccia: **chiudere non\r\n * annulla**.\r\n *\r\n * Il nodo **non arriva per input**: si rideriva da `selectedNodeName()`, cosi' se cambia sotto\r\n * (una rinomina) la dialog lo segue invece di guardare un nome che non esiste piu'.\r\n */\r\n\r\nimport {\r\n ChangeDetectionStrategy,\r\n Component,\r\n ElementRef,\r\n computed,\r\n effect,\r\n inject,\r\n viewChild,\r\n} from '@angular/core';\r\n\r\nimport { DpeDocumentStore } from '../../core/dpe-document.store';\r\nimport { dpeFindNode } from '../../core/dpe-graph';\r\nimport { dpeNodeCategory } from '../../core/dpe-node-catalog';\r\nimport { dpeNodeIconFamilyClass } from '../../core/dpe-node-icons';\r\nimport { DpeNodeFactsComponent } from '../inspector/dpe-node-facts.component';\r\nimport { DpeNodeInspectorComponent } from '../inspector/dpe-node-inspector.component';\r\nimport { DpeNodeIconComponent } from '../shared/dpe-node-icon.component';\r\n\r\n/**\r\n * Contatore di istanza per l'id dell'etichetta. Serve perche' `aria-labelledby` punta a un id, e\r\n * due editor nella stessa pagina sono due dialog: con un id fisso il documento avrebbe due\r\n * elementi con lo stesso id e il nome accessibile della seconda finestra sarebbe quello della\r\n * prima. Non e' una preferenza di stile — un id duplicato e' un difetto che solo uno screen\r\n * reader mostra.\r\n */\r\nlet nextLabelId = 0;\r\n\r\n@Component({\r\n selector: 'dpe-node-dialog',\r\n standalone: true,\r\n imports: [DpeNodeFactsComponent, DpeNodeInspectorComponent, DpeNodeIconComponent],\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n template: `\r\n <!--\r\n L’elemento dialog sta **sempre** nel template, e solo il contenuto e’ condizionato: cosi’\r\n viewChild.required non puo’ fallire e l’effect che apre non deve difendersi da un riferimento\r\n assente. Una dialog vuota e chiusa non rende niente — il foglio dell’agente utente le da’\r\n display: none.\r\n -->\r\n <dialog\r\n #dlg\r\n class=\"dpe-dialog\"\r\n [attr.aria-labelledby]=\"labelId\"\r\n (close)=\"onClose()\"\r\n (cancel)=\"onCancel()\"\r\n (click)=\"onDialogClick($event)\"\r\n (pointerdown)=\"onPointerDown($event)\"\r\n >\r\n @if (located(); as entry) {\r\n <header class=\"dpe-dialog__head\">\r\n <!--\r\n Quarto posto in cui compare il tipo di nodo (palette, card del canvas, ispettore, qui):\r\n stessa icona e stessa classe di famiglia, calcolate dalle stesse funzioni. Un tipo che\r\n cambia aspetto da un posto all’altro e’ un tipo che non si riconosce.\r\n -->\r\n <span class=\"dpe-dialog__icon\" [class]=\"'dpe-dialog__icon ' + familyClass()\">\r\n <dpe-node-icon [collection]=\"entry.collection\" />\r\n </span>\r\n <strong class=\"dpe-dialog__label\" [id]=\"labelId\">\r\n {{ entry.node.label || entry.node.name }}\r\n </strong>\r\n <span class=\"dpe-dialog__category\">{{ categoryLabel() }}</span>\r\n <code class=\"dpe-dialog__name\">{{ entry.node.name }}</code>\r\n <button\r\n type=\"button\"\r\n class=\"dpe-dialog__btn dpe-dialog__btn--icon\"\r\n aria-label=\"Chiudi la finestra di modifica\"\r\n title=\"Chiudi (Esc). Le modifiche sono già nel documento.\"\r\n (click)=\"store.closeNodeEditor()\"\r\n >\r\n ✕\r\n </button>\r\n </header>\r\n\r\n <div class=\"dpe-dialog__body\">\r\n <!-- Colonna larga: i campi. E’ la ragione per cui questa finestra esiste. -->\r\n <div class=\"dpe-dialog__main\">\r\n <dpe-node-inspector />\r\n </div>\r\n\r\n <!--\r\n Colonna stretta: i fatti dal backend su **questo** nodo. Non entra cio’ che riguarda il\r\n grafo — diagnostiche di tutta la definizione, schema di tutti i nodi e SQL generato\r\n hanno gia’ le loro linguette nel pannello inferiore.\r\n -->\r\n <aside class=\"dpe-dialog__side\">\r\n <dpe-node-facts [nodeName]=\"entry.node.name\" />\r\n </aside>\r\n </div>\r\n\r\n <footer class=\"dpe-dialog__foot\">\r\n <!--\r\n «Dopo aver chiuso» non e’ una precisazione di cortesia: con showModal() il fondo e’\r\n **inerte**, quindi il ↶ della barra non e’ premibile mentre questa frase si legge. E’\r\n il comportamento nativo per cui la dialog e’ stata scelta (ADR 0026) — fondo inerte,\r\n Esc e focus trap senza scrivere un overlay — quindi a dover dire il vero e’ la frase,\r\n non il comportamento. Instradare l’annullamento globale da qui dentro sarebbe piu’\r\n comodo e aprirebbe una domanda che va decisa prima: cosa fa questa finestra se\r\n l’annullamento fa sparire il nodo che sta mostrando.\r\n -->\r\n <p class=\"dpe-dialog__note\">\r\n Le modifiche sono <strong>già nel documento</strong>: chiudere non le annulla. Una volta\r\n chiusa questa finestra si torna indietro con ↶ nella barra.\r\n </p>\r\n <button\r\n type=\"button\"\r\n class=\"dpe-dialog__btn dpe-dialog__btn--danger\"\r\n title=\"Elimina il nodo: i riferimenti a valle restano e diventano riferimenti rotti. Eliminare chiude questa finestra, e da lì il nodo si rimette con ↶ nella barra.\"\r\n (click)=\"store.removeNode(entry.node.name)\"\r\n >\r\n Elimina il nodo\r\n </button>\r\n <button type=\"button\" class=\"dpe-dialog__btn\" (click)=\"store.closeNodeEditor()\">\r\n Chiudi\r\n </button>\r\n </footer>\r\n }\r\n </dialog>\r\n `,\r\n})\r\nexport class DpeNodeDialogComponent {\r\n protected readonly store = inject(DpeDocumentStore);\r\n\r\n private readonly dialogRef = viewChild.required<ElementRef<HTMLDialogElement>>('dlg');\r\n\r\n protected readonly labelId = `dpe-node-dialog-label-${(nextLabelId += 1)}`;\r\n\r\n /**\r\n * Il nodo corrente, **riderivato** dallo store come fa il dispatcher degli ispettori. Non si\r\n * conserva il nome: se il nodo viene rinominato, `selectedNodeName()` porta gia' il nome nuovo.\r\n */\r\n protected readonly located = computed(() => {\r\n const name = this.store.selectedNodeName();\r\n return name === undefined ? undefined : dpeFindNode(this.store.definition(), name);\r\n });\r\n\r\n protected readonly categoryLabel = computed(() => {\r\n const entry = this.located();\r\n return entry ? dpeNodeCategory(entry.collection).label : '';\r\n });\r\n\r\n protected readonly familyClass = computed(() => {\r\n const entry = this.located();\r\n return entry ? dpeNodeIconFamilyClass(entry.collection) : '';\r\n });\r\n\r\n /**\r\n * Vero quando il fondo ha ricevuto il `pointerdown` di questo clic. Vedi `onDialogClick`: la\r\n * verifica di coppia non e' prudenza, e' la correzione di un difetto reale.\r\n */\r\n private pointerDownWasOnBackdrop = false;\r\n\r\n constructor() {\r\n effect(() => {\r\n const el = this.dialogRef().nativeElement;\r\n const shouldBeOpen = this.store.isNodeEditorOpen() && this.located() !== undefined;\r\n // La guardia su `el.open` **non e' cosmetica**: `showModal()` su una dialog gia' aperta\r\n // lancia `InvalidStateError`, e questo effect riesegue a ogni mutazione del documento\r\n // (legge `located()`). Senza, aprire e modificare un campo basterebbe a far comparire\r\n // l'eccezione in console.\r\n if (shouldBeOpen && !el.open) {\r\n el.showModal();\r\n } else if (!shouldBeOpen && el.open) {\r\n el.close();\r\n }\r\n });\r\n }\r\n\r\n /**\r\n * **Esc.** Il browser chiede di chiudere e la finestra e' ancora aperta: la richiesta riguarda\r\n * per definizione la sessione corrente, quindi si chiude e non serve nessuna verifica.\r\n *\r\n * Perche' `cancel` e' legato, contro cio' che sembra sufficiente: in Chrome 151 l'evento `close`\r\n * **arriva molto in ritardo** (vedi `onClose`), quindi con il solo `close` lo store restava\r\n * «aperta» dopo Esc — e il sintomo era che **riaprire lo stesso nodo non faceva niente**: il\r\n * signal non cambiava, l'effect non rieseguiva, `showModal()` non veniva mai chiamato. Sul\r\n * bottone del piede non si vedeva, perche' quella via passa dallo store.\r\n *\r\n * `cancel` sarebbe annullabile: non si annulla. La chiusura dell'elemento arriva comunque dal\r\n * browser, e la guardia su `el.open` nell'effect regge in entrambi gli ordini.\r\n */\r\n protected onCancel(): void {\r\n this.store.closeNodeEditor();\r\n }\r\n\r\n /**\r\n * **L'elemento si e' chiuso.** La guardia non e' prudenza: Chrome accoda `close` come task\r\n * dell'elemento e lo consegna anche **dopo** una riapertura — misurato nella demo, la traccia\r\n * degli eventi mostra `close` con `open: true` subito dopo un `showModal()`. Senza la guardia\r\n * quel `close` in ritardo chiudeva la finestra appena aperta, e il sintomo era una dialog che\r\n * «non si apre a volte»: sparisce da sola dopo essere comparsa, e con la finestra aperta si\r\n * riapriva solo alla prima modifica del documento (l'unica cosa che faceva rieseguire l'effect).\r\n *\r\n * Se l'elemento risulta **aperto**, questo `close` riguarda una sessione precedente e si ignora.\r\n * Se risulta chiuso, la notifica e' di questa sessione — e allineare qui lo store e' anche cio'\r\n * che ripara una divergenza, se il browser chiudesse la finestra per vie sue.\r\n */\r\n protected onClose(): void {\r\n if (this.dialogRef().nativeElement.open) {\r\n return;\r\n }\r\n this.store.closeNodeEditor();\r\n }\r\n\r\n protected onPointerDown(event: Event): void {\r\n this.pointerDownWasOnBackdrop = event.target === this.dialogRef().nativeElement;\r\n }\r\n\r\n /**\r\n * Chiusura al clic sul fondo, con la **verifica di coppia** e non con il solo controllo sul\r\n * click. I clic sul velo sono recapitati all'elemento `<dialog>` stesso — ma anche un\r\n * trascinamento di selezione del testo che **inizia** dentro una casella e **finisce** fuori\r\n * produce un `click` con `target` uguale alla dialog. Con la sola verifica sul click, selezionare\r\n * del testo chiuderebbe la finestra.\r\n */\r\n protected onDialogClick(event: Event): void {\r\n if (this.pointerDownWasOnBackdrop && event.target === this.dialogRef().nativeElement) {\r\n this.store.closeNodeEditor();\r\n }\r\n }\r\n}\r\n","/**\r\n * La palette dei nodi.\r\n *\r\n * Il trascinamento verso il canvas usa `fExternalItem` di @foblex/flow: il rilascio emette\r\n * `fCreateNode` con il `fData` e la posizione **gia' nel sistema di coordinate del canvas**.\r\n * Convertirla a mano — che e' cio' che servirebbe con un drag generico — vorrebbe dire rifare la\r\n * trasformazione di zoom e panoramica della libreria, cioe' riscrivere la parte piu' facile da\r\n * sbagliare senza guadagnarci niente. Il **clic** resta la via da tastiera: la palette e'\r\n * navigabile con Tab e Invio, e chi non usa il mouse crea comunque un nodo.\r\n *\r\n * Il catalogo e' **descrittivo**: etichette e numero di sorgenti servono a rendere, non a\r\n * validare. Che un'unione con una sola sorgente sia sbagliata lo dice il backend.\r\n *\r\n * La voce sta su **una riga** — pastiglia con l'icona, etichetta — e la descrizione resta nel\r\n * `title`. La versione precedente ne faceva un paragrafo di tre righe: undici categorie non\r\n * entravano nel riquadro e sette si vedevano senza scorrere, cioe' l'elenco non era un elenco.\r\n */\r\n\r\nimport { ChangeDetectionStrategy, Component, computed, output, signal } from '@angular/core';\r\nimport { FFlowModule } from '@foblex/flow';\r\n\r\nimport { DPE_NODE_CATEGORIES, DpeNodeCategory } from '../../core/dpe-node-catalog';\r\nimport { dpeNodeIconFamilyClass } from '../../core/dpe-node-icons';\r\nimport { DpeNodeCollectionName } from '../../model/dpe-definition.model';\r\nimport { DpeNodeIconComponent } from '../shared/dpe-node-icon.component';\r\n\r\n/** Un gruppo dell'elenco: intestazione e voci. Presentazione, non semantica. */\r\nexport interface DpeNodePaletteGroup {\r\n key: 'sources' | 'transforms' | 'sinks';\r\n label: string;\r\n categories: readonly DpeNodeCategory[];\r\n}\r\n\r\n@Component({\r\n selector: 'dpe-node-palette',\r\n standalone: true,\r\n imports: [FFlowModule, DpeNodeIconComponent],\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n template: `\r\n <h3>Nodi</h3>\r\n <input\r\n type=\"search\"\r\n placeholder=\"Cerca un nodo\"\r\n aria-label=\"Cerca un nodo\"\r\n (input)=\"filter.set(text($event))\"\r\n />\r\n\r\n @for (group of groups(); track group.key) {\r\n <p class=\"dpe-palette__group\">{{ group.label }}</p>\r\n @for (category of group.categories; track category.collection) {\r\n <!--\r\n fExternalItem rende la voce trascinabile sul canvas; fData porta il nome della\r\n collezione, che e’ l’unica cosa che serve per creare il nodo.\r\n -->\r\n <button\r\n type=\"button\"\r\n class=\"dpe-palette__item\"\r\n fExternalItem\r\n [fExternalItemId]=\"category.collection\"\r\n [fData]=\"category.collection\"\r\n [title]=\"titleOf(category)\"\r\n (click)=\"nodeRequested.emit(category.collection)\"\r\n >\r\n <!--\r\n Un solo figlio dentro l’elemento che la libreria tocca: e’ qui che sta il layout della\r\n riga, e non dipende dal display che @foblex/flow riscrive dopo un trascinamento.\r\n -->\r\n <span class=\"dpe-palette__body\">\r\n <span class=\"dpe-palette__icon\" [class]=\"'dpe-palette__icon ' + familyClass(category)\">\r\n <dpe-node-icon [collection]=\"category.collection\" />\r\n </span>\r\n <span class=\"dpe-palette__label\">{{ category.label }}</span>\r\n @if (category.isNotCompilableInV1) {\r\n <span class=\"dpe-palette__flag\">v1</span>\r\n }\r\n </span>\r\n </button>\r\n }\r\n }\r\n\r\n @if (visible().length === 0) {\r\n <p class=\"dpe-palette__empty\">Nessun nodo corrisponde alla ricerca.</p>\r\n }\r\n `,\r\n})\r\nexport class DpeNodePaletteComponent {\r\n /** Clic sulla voce: il nodo si crea al centro dell'area, non dove stava il puntatore. */\r\n readonly nodeRequested = output<DpeNodeCollectionName>();\r\n\r\n protected readonly filter = signal('');\r\n\r\n readonly visible = computed<readonly DpeNodeCategory[]>(() => {\r\n const needle = this.filter().trim().toLowerCase();\r\n if (needle.length === 0) {\r\n return DPE_NODE_CATEGORIES;\r\n }\r\n return DPE_NODE_CATEGORIES.filter(\r\n (category) =>\r\n category.label.toLowerCase().includes(needle) ||\r\n category.description.toLowerCase().includes(needle),\r\n );\r\n });\r\n\r\n /**\r\n * Tre gruppi, nell'ordine in cui si costruisce una pipeline: da dove entrano i dati, cosa ci\r\n * si fa, dove finiscono. I gruppi vuoti non compaiono, altrimenti una ricerca lascerebbe sullo\r\n * schermo intestazioni senza voci.\r\n */\r\n readonly groups = computed<readonly DpeNodePaletteGroup[]>(() => {\r\n const visible = this.visible();\r\n const groups: DpeNodePaletteGroup[] = [\r\n {\r\n key: 'sources',\r\n label: 'Sorgenti',\r\n categories: visible.filter((category) => category.isSource),\r\n },\r\n {\r\n key: 'transforms',\r\n label: 'Trasformazioni',\r\n categories: visible.filter((category) => !category.isSource && !category.isSink),\r\n },\r\n {\r\n key: 'sinks',\r\n label: 'Scritture',\r\n categories: visible.filter((category) => category.isSink),\r\n },\r\n ];\r\n return groups.filter((group) => group.categories.length > 0);\r\n });\r\n\r\n protected familyClass(category: DpeNodeCategory): string {\r\n return dpeNodeIconFamilyClass(category.collection);\r\n }\r\n\r\n /** Il testo che la descrizione non mostra piu' nel corpo della voce. */\r\n protected titleOf(category: DpeNodeCategory): string {\r\n return category.isNotCompilableInV1\r\n ? `${category.description}\\nModellato ma non compilabile nella versione 1.`\r\n : category.description;\r\n }\r\n\r\n protected text(event: Event): string {\r\n return (event.target as HTMLInputElement).value;\r\n }\r\n}\r\n","/**\r\n * Anteprima dell'SQL generato, in **sola lettura**.\r\n *\r\n * E' lo strumento diagnostico principale per chi costruisce una pipeline: e' l'unico posto in\r\n * cui si vede cosa diventera' il grafo. Tre cose che l'interfaccia dice esplicitamente, perche'\r\n * nessuna e' ovvia guardando il testo:\r\n *\r\n * - i writeback `Database` producono un'istruzione **per l'host**, che la libreria non esegue;\r\n * - i writeback `Json`/`Csv` portano l'SQL di **lettura** con cui l'artefatto viene materializzato;\r\n * - i parametri restano **parametri**: il testo mostra i marcatori del dialetto e i valori stanno\r\n * accanto. Non si interpolano nel testo nemmeno per mostrarlo — un'anteprima che mostra SQL\r\n * concatenato insegna a scriverlo.\r\n *\r\n * Se l'host non implementa `compilePreview` il comando e' disabilitato e questo pannello lo dice:\r\n * degrada l'anteprima, non l'editor.\r\n */\r\n\r\nimport { ChangeDetectionStrategy, Component, computed, inject } from '@angular/core';\r\n\r\nimport { DpeDocumentStore } from '../../core/dpe-document.store';\r\nimport { DpeValidationService } from '../../core/dpe-validation.service';\r\nimport { DpeArtifactPlan, DpeCompiledWriteback } from '../../model/dpe-api.model';\r\n\r\n/**\r\n * I tre soli fatti che un writeback compilato e un **piano di artefatto** (ADR 0029) hanno in\r\n * comune, e gli unici che questo pannello mostra. Tipizzare la sezione sul minimo condiviso\r\n * evita un'unione che il template dovrebbe poi discriminare per rendere le stesse tre cose: qui\r\n * cambia il tipo, non il comportamento.\r\n */\r\ntype DpeSqlPreviewItem = Pick<\r\n DpeCompiledWriteback & DpeArtifactPlan,\r\n 'writebackName' | 'sequence' | 'statement'\r\n>;\r\n\r\ninterface DpeSqlSection {\r\n title: string;\r\n hint: string;\r\n writebacks: readonly DpeSqlPreviewItem[];\r\n}\r\n\r\n@Component({\r\n selector: 'dpe-sql-preview',\r\n standalone: true,\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n template: `\r\n <div class=\"dpe-sql__bar\">\r\n <button\r\n type=\"button\"\r\n class=\"dpe-sql__btn\"\r\n [disabled]=\"!validation.isPreviewAvailable() || validation.isCompiling()\"\r\n (click)=\"validation.requestPreview()\"\r\n >\r\n @if (validation.isCompiling()) {\r\n Compilazione in corso…\r\n } @else {\r\n Genera anteprima\r\n }\r\n </button>\r\n @if (!validation.isPreviewAvailable()) {\r\n <span class=\"dpe-sql__note\">\r\n Anteprima non disponibile su questo ambiente: l’host non espone la compilazione.\r\n </span>\r\n } @else {\r\n <span class=\"dpe-sql__note\">Sola lettura: la libreria non esegue nulla.</span>\r\n }\r\n </div>\r\n\r\n @if (result(); as compiled) {\r\n @if (compiled.hasErrors) {\r\n <p class=\"dpe-sql__empty\">\r\n La compilazione si e’ fermata: ci sono errori di validazione, e nessun artefatto parziale\r\n viene prodotto. Guarda il pannello delle diagnostiche.\r\n </p>\r\n }\r\n\r\n @for (section of sections(); track $index) {\r\n @if (section.writebacks.length > 0) {\r\n <div class=\"dpe-sql__section\">\r\n <h4 class=\"dpe-sql__title\">{{ section.title }}</h4>\r\n <p class=\"dpe-sql__note\">{{ section.hint }}</p>\r\n @for (writeback of section.writebacks; track $index) {\r\n <div>\r\n <p class=\"dpe-sql__note\">\r\n <strong>{{ writeback.writebackName }}</strong> · sequenza {{ writeback.sequence }}\r\n </p>\r\n <pre>{{ writeback.statement.text }}</pre>\r\n @if (writeback.statement.parameters?.length) {\r\n <table>\r\n <thead>\r\n <tr>\r\n <th>Parametro</th>\r\n <th>Tipo</th>\r\n <th>Valore</th>\r\n </tr>\r\n </thead>\r\n <tbody>\r\n @for (parameter of writeback.statement.parameters ?? []; track $index) {\r\n <tr>\r\n <td>{{ parameter.name }}</td>\r\n <td>{{ parameter.type }}</td>\r\n <td>{{ display(parameter.value) }}</td>\r\n </tr>\r\n }\r\n </tbody>\r\n </table>\r\n }\r\n </div>\r\n }\r\n </div>\r\n }\r\n }\r\n\r\n @if (isEmpty()) {\r\n <p class=\"dpe-sql__empty\">Nessuna istruzione prodotta.</p>\r\n }\r\n } @else {\r\n <p class=\"dpe-sql__empty\">Nessuna anteprima richiesta.</p>\r\n }\r\n `,\r\n})\r\nexport class DpeSqlPreviewComponent {\r\n private readonly store = inject(DpeDocumentStore);\r\n protected readonly validation = inject(DpeValidationService);\r\n\r\n readonly result = computed(() => this.store.compileResult());\r\n\r\n readonly sections = computed<DpeSqlSection[]>(() => {\r\n const compiled = this.result();\r\n return [\r\n {\r\n title: 'Scritture su database',\r\n hint: 'Istruzione consegnata all’host, che la esegue: la libreria non scrive.',\r\n writebacks: compiled?.databaseWritebacks ?? [],\r\n },\r\n {\r\n title: 'Artefatti (JSON / CSV)',\r\n hint: 'SQL di lettura con cui il file viene materializzato, non ancora eseguito.',\r\n writebacks: compiled?.fileWritebacks ?? [],\r\n },\r\n ];\r\n });\r\n\r\n readonly isEmpty = computed(() =>\r\n this.sections().every((section) => section.writebacks.length === 0),\r\n );\r\n\r\n /** Il valore di un parametro e' opaco: si mostra, non si converte. */\r\n protected display(value: unknown): string {\r\n if (value === null || value === undefined) {\r\n return 'NULL';\r\n }\r\n return typeof value === 'object' ? JSON.stringify(value) : String(value);\r\n }\r\n}\r\n","/**\r\n * La vista dello schema: colonne in ingresso e in uscita, nodo per nodo.\r\n *\r\n * Tutto quello che si vede qui arriva da `Schemas` di `validate` (**I4**: propagazione pura e\r\n * deterministica, del backend). Il frontend non deduce niente — se il backend non ha ancora\r\n * risposto, la vista e' vuota e lo dice.\r\n *\r\n * Le colonne **in ingresso** sono lo schema dei nodi a monte, e servono accanto a quelle in\r\n * uscita per una cosa sola: vedere dove un **alias** cambia il nome di una colonna (**I5**) e\r\n * cosa arriva a valle con quel nome nuovo. Le colonne rinominate sono marcate: e' il punto in cui\r\n * un riferimento a valle sbaglia nome piu' spesso.\r\n */\r\n\r\nimport { ChangeDetectionStrategy, Component, computed, inject } from '@angular/core';\r\n\r\nimport { DpeDocumentStore } from '../../core/dpe-document.store';\r\nimport { dpeAllNodes, dpeNodeSourceReferences } from '../../core/dpe-graph';\r\nimport { dpeNodeCategory } from '../../core/dpe-node-catalog';\r\nimport { DpeColumnInfo } from '../../model/dpe-api.model';\r\n\r\ninterface DpeSchemaRow {\r\n nodeName: string;\r\n categoryLabel: string;\r\n isSelected: boolean;\r\n isCompilable: boolean;\r\n hasSchema: boolean;\r\n inputColumns: readonly DpeColumnInfo[];\r\n outputColumns: readonly DpeColumnInfo[];\r\n /** Colonne in uscita che non esistono in ingresso: alias nuovi o colonne prodotte dal nodo. */\r\n introduced: ReadonlySet<string>;\r\n /** Colonne in ingresso che non arrivano in uscita: eliminate o rinominate. */\r\n dropped: readonly string[];\r\n}\r\n\r\n@Component({\r\n selector: 'dpe-schema-panel',\r\n standalone: true,\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n template: `\r\n @if (!hasResult()) {\r\n <p class=\"dpe-schema__empty\">In attesa della propagazione dello schema dal backend…</p>\r\n }\r\n\r\n @for (row of rows(); track $index) {\r\n <section class=\"dpe-schema__node\" [class.dpe-schema__node--selected]=\"row.isSelected\">\r\n <div class=\"dpe-schema__head\">\r\n <span class=\"dpe-schema__name\">{{ row.nodeName }}</span>\r\n <span class=\"dpe-schema__category\">{{ row.categoryLabel }}</span>\r\n @if (!row.isCompilable) {\r\n <span class=\"dpe-schema__tag\" title=\"Valido, ma non traducibile in SQL\">non compilabile</span>\r\n }\r\n </div>\r\n\r\n @if (row.inputColumns.length > 0) {\r\n <div class=\"dpe-schema__line\">\r\n <span class=\"dpe-schema__label\">In ingresso</span>\r\n @for (column of row.inputColumns; track $index) {\r\n <span\r\n class=\"dpe-col\"\r\n [class.dpe-col--nullable]=\"column.isNullable\"\r\n [class.dpe-col--dropped]=\"row.dropped.includes(column.name)\"\r\n [title]=\"column.type.kind\"\r\n >\r\n {{ column.name }}\r\n </span>\r\n }\r\n </div>\r\n }\r\n\r\n <div class=\"dpe-schema__line\">\r\n <span class=\"dpe-schema__label\">In uscita</span>\r\n @if (!row.hasSchema) {\r\n <!--\r\n «Ignoto», non «nessuna»: il backend non ha derivato lo schema di questo nodo, e\r\n l’elenco vuoto qui accanto non afferma zero colonne (ADR 0041).\r\n -->\r\n <span\r\n class=\"dpe-schema__empty\"\r\n title=\"Il backend non ha potuto derivare lo schema di questo nodo: l’elenco vuoto non afferma zero colonne\"\r\n >schema ignoto</span\r\n >\r\n }\r\n @for (column of row.outputColumns; track $index) {\r\n <span\r\n class=\"dpe-col\"\r\n [class.dpe-col--nullable]=\"column.isNullable\"\r\n [class.dpe-col--new]=\"row.introduced.has(column.name.toLowerCase())\"\r\n [title]=\"column.type.kind + (column.isNullable ? ' · nullabile' : ' · non nulla')\"\r\n >\r\n {{ column.name }}\r\n </span>\r\n }\r\n </div>\r\n </section>\r\n }\r\n\r\n @if (rows().length > 0) {\r\n <p class=\"dpe-schema__empty\">\r\n In evidenza le colonne che il nodo <strong>introduce</strong> (alias nuovi e colonne\r\n calcolate) e barrate quelle che non arrivano a valle. Tratteggiate le nullabili.\r\n </p>\r\n }\r\n `,\r\n})\r\nexport class DpeSchemaPanelComponent {\r\n private readonly store = inject(DpeDocumentStore);\r\n\r\n readonly hasResult = computed(() => this.store.validateResult() !== undefined);\r\n\r\n readonly rows = computed<DpeSchemaRow[]>(() => {\r\n const definition = this.store.definition();\r\n const schemas = this.store.schemaByNode();\r\n const selected = this.store.selectedNodeName();\r\n const executionOrder = this.store.executionOrder();\r\n\r\n const located = dpeAllNodes(definition);\r\n // L'ordine e' quello **topologico** del backend quando c'e': leggere lo schema nell'ordine\r\n // in cui i dati scorrono e' l'unico modo di seguire un alias fino al sink.\r\n const ordered =\r\n executionOrder.length > 0\r\n ? [...located].sort(\r\n (left, right) =>\r\n indexOrLast(executionOrder, left.node.name) -\r\n indexOrLast(executionOrder, right.node.name),\r\n )\r\n : located;\r\n\r\n return ordered.map((entry) => {\r\n const schema = schemas.get(entry.node.name);\r\n const outputColumns = schema?.columns ?? [];\r\n const inputColumns: DpeColumnInfo[] = [];\r\n const seen = new Set<string>();\r\n for (const reference of dpeNodeSourceReferences(entry)) {\r\n if (!reference.isEdge || !reference.sourceName) {\r\n continue;\r\n }\r\n for (const column of schemas.get(reference.sourceName)?.columns ?? []) {\r\n const key = column.name.toLowerCase();\r\n if (!seen.has(key)) {\r\n seen.add(key);\r\n inputColumns.push(column);\r\n }\r\n }\r\n }\r\n const outputNames = new Set(outputColumns.map((column) => column.name.toLowerCase()));\r\n return {\r\n nodeName: entry.node.name,\r\n categoryLabel: dpeNodeCategory(entry.collection).label,\r\n isSelected: entry.node.name === selected,\r\n isCompilable: schema?.isCompilable !== false,\r\n // Si **legge** dal campo, e non si deduce dalla presenza della voce: da ADR 0041 una voce\r\n // c'e' per ogni nodo della definizione, quindi `schema !== undefined` era vero anche a\r\n // schema ignoto e la riga taceva proprio dove doveva parlare. `=== true` perche' assente\r\n // significa `false`: l'ospite che omette i falsi non manda affatto la proprieta'.\r\n hasSchema: schema?.hasSchema === true,\r\n inputColumns,\r\n outputColumns,\r\n introduced: new Set(\r\n outputColumns\r\n .map((column) => column.name.toLowerCase())\r\n .filter((name) => !seen.has(name)),\r\n ),\r\n dropped: inputColumns\r\n .filter((column) => !outputNames.has(column.name.toLowerCase()))\r\n .map((column) => column.name),\r\n };\r\n });\r\n });\r\n}\r\n\r\n/** I nodi fuori dall'ordine di esecuzione (irraggiungibili) finiscono in coda, non in testa. */\r\nfunction indexOrLast(order: readonly string[], name: string): number {\r\n const index = order.indexOf(name);\r\n return index < 0 ? Number.MAX_SAFE_INTEGER : index;\r\n}\r\n","/**\r\n * Il componente radice dell'editor: `<dpe-builder>`.\r\n *\r\n * Tiene i provider — store del documento, layout, validazione, metadati — **a livello di\r\n * componente** e non della root dell'applicazione: due editor sulla stessa pagina sono due\r\n * documenti indipendenti, con la loro cronologia e la loro selezione.\r\n *\r\n * Non conosce HTTP: tutto il traffico passa da `DpeBuilderApi`, che l'ospite fornisce.\r\n */\r\n\r\nimport {\r\n ChangeDetectionStrategy,\r\n Component,\r\n Signal,\r\n computed,\r\n effect,\r\n inject,\r\n input,\r\n output,\r\n signal,\r\n untracked,\r\n viewChild,\r\n} from '@angular/core';\r\n\r\n\r\nimport { DpeDocumentStore } from '../core/dpe-document.store';\r\nimport { DpeFormulaCatalogStore } from '../core/dpe-formula-catalog.store';\r\nimport { DpeLayoutService } from '../core/dpe-layout.service';\r\nimport { DpeMetadataStore } from '../core/dpe-metadata.store';\r\nimport { DpeValidationService } from '../core/dpe-validation.service';\r\nimport { dpeHasCompleteLayout } from '../core/dpe-node-layout';\r\nimport { dpeAllNodes } from '../core/dpe-graph';\r\nimport { DPE_DIALECT_LABELS } from '../core/dpe-node-catalog';\r\nimport { DpeDefinition, DpeNodeCollectionName, SqlDialectKind } from '../model/dpe-definition.model';\r\nimport { DpeCanvasComponent } from './canvas/dpe-canvas.component';\r\nimport { DpeDiagnosticsPanelComponent } from './diagnostics/dpe-diagnostics-panel.component';\r\nimport { DpeNodeDialogComponent } from './dialog/dpe-node-dialog.component';\r\nimport { DpeNodePaletteComponent } from './palette/dpe-node-palette.component';\r\nimport { DpeSqlPreviewComponent } from './preview/dpe-sql-preview.component';\r\nimport { DpeSchemaPanelComponent } from './schema/dpe-schema-panel.component';\r\n\r\n/** Le tre viste in basso: una alla volta, perche' guardano la stessa cosa da tre angoli. */\r\ntype DpeBottomTab = 'diagnostics' | 'schema' | 'sql';\r\n\r\n@Component({\r\n selector: 'dpe-builder',\r\n standalone: true,\r\n imports: [\r\n DpeCanvasComponent,\r\n DpeDiagnosticsPanelComponent,\r\n DpeNodeDialogComponent,\r\n DpeNodePaletteComponent,\r\n DpeSchemaPanelComponent,\r\n DpeSqlPreviewComponent,\r\n ],\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n /*\r\n * Provider **del componente**, mai della root: due editor sulla stessa pagina sono due documenti\r\n * indipendenti, con la loro cronologia, la loro selezione e — da LF6 — la loro cache di\r\n * cataloghi di formule, che e' per nodo e quindi per documento.\r\n */\r\n providers: [\r\n DpeDocumentStore,\r\n DpeLayoutService,\r\n DpeValidationService,\r\n DpeMetadataStore,\r\n DpeFormulaCatalogStore,\r\n ],\r\n /**\r\n * `Ctrl+S` sul **componente**, non su `document`: due editor nella stessa pagina non devono\r\n * salvarsi a vicenda, e un ascoltatore globale installato dalla libreria intercetterebbe la\r\n * scorciatoia anche quando l'utente sta lavorando altrove nell'applicazione ospite.\r\n */\r\n host: { '(keydown)': 'onKeyDown($event)' },\r\n templateUrl: './dpe-builder.component.html',\r\n})\r\nexport class DpeBuilderComponent {\r\n protected readonly store = inject(DpeDocumentStore);\r\n protected readonly validation = inject(DpeValidationService);\r\n private readonly layout = inject(DpeLayoutService);\r\n\r\n /** La definizione da editare. Cambiarne l'identita' ricarica il documento. */\r\n readonly definition = input.required<DpeDefinition>();\r\n\r\n readonly isEditable = input(true);\r\n\r\n /**\r\n * Emessa a ogni mutazione. L'ospite **non deve** rimandarla dentro `definition`: sarebbe un\r\n * ciclo, e ricaricare azzera la cronologia. Serve per salvare, non per pilotare l'editor.\r\n */\r\n readonly definitionChange = output<DpeDefinition>();\r\n\r\n protected readonly dialectLabels = DPE_DIALECT_LABELS;\r\n\r\n protected readonly bottomTab = signal<DpeBottomTab>('diagnostics');\r\n\r\n private readonly canvas = viewChild(DpeCanvasComponent);\r\n\r\n /**\r\n * Il dialetto **della definizione**: e' quello con cui il backend compilera' (ADR 0009), quindi\r\n * e' quello che il badge mostra. In sola lettura: nessun comando dell'interfaccia lo cambia\r\n * (ADR 0017), perche' il database di destinazione e' un fatto dell'ambiente e non una\r\n * preferenza dell'autore.\r\n */\r\n protected readonly dialect = computed(() => this.store.definition().dialect);\r\n\r\n /**\r\n * Disallineamento fra cio' che l'ambiente dichiara e cio' che la definizione porta. Si\r\n * **segnala** e non si corregge: correggere vorrebbe dire cambiare le capacita' con cui la\r\n * definizione verra' compilata, che e' dell'host. Assente `getEnvironment`, non c'e' niente da\r\n * confrontare.\r\n */\r\n protected readonly dialectMismatch = computed<SqlDialectKind | undefined>(() => {\r\n const declared = this.validation.environmentDialect();\r\n return declared !== undefined && declared !== this.dialect() ? declared : undefined;\r\n });\r\n\r\n /**\r\n * Il documento porta una chiave utilizzabile. Il nome **e'** la chiave del catalogo, e la rotta di\r\n * aggiornamento e' `PUT /definitions/{name}`: senza nome non c'e' niente da aggiornare, e una\r\n * definizione nuova si crea dal catalogo (ADR 0019), non da qui.\r\n *\r\n * Sta qui e non solo nel client per la ragione che questo progetto applica alle primitive\r\n * assenti: un comando disabilitato che dice il motivo e' meglio di un comando attivo che fallisce\r\n * alla pressione. Il client rifiuta comunque — e' lui che compone l'URL, e un ospite che chiami\r\n * `save()` per vie proprie deve trovare la stessa risposta.\r\n */\r\n protected readonly hasKey = computed(() => (this.store.definition().name ?? '').trim().length > 0);\r\n\r\n /**\r\n * Il documento ricevuto **non e' una definizione**, detto subito e a schermo.\r\n *\r\n * L'unico ingresso e' l'input `definition`, e finora l'editor accettava qualunque oggetto: con un\r\n * documento della forma sbagliata mostrava un canvas vuoto — che sembra una definizione senza\r\n * nodi, cioe' uno stato legittimo — e il difetto ricompariva molto piu' tardi come un salvataggio\r\n * verso una chiave che il client doveva inventare. Le tre forme che arrivano davvero al posto del\r\n * documento sono l'involucro di lettura (`{ definition, diagnostics }`), il **sommario** del\r\n * catalogo (ha `name` ma non `formatVersion` ne' i nodi) e una risposta in PascalCase.\r\n *\r\n * Si controllano le due proprieta' obbligatorie e nient'altro: una definizione senza nodi e'\r\n * legittima, e verificare di piu' sarebbe validare — che e' del backend (I6).\r\n */\r\n protected readonly documentProblem = computed<string | undefined>(() => {\r\n const document = this.store.definition() as Partial<DpeDefinition> | null | undefined;\r\n if (typeof document?.formatVersion !== 'number') {\r\n return 'manca `formatVersion`';\r\n }\r\n if (!this.hasKey()) {\r\n return \"manca `name`, che e' la chiave del catalogo\";\r\n }\r\n return undefined;\r\n });\r\n\r\n protected readonly canSave = computed(\r\n () =>\r\n this.validation.isSaveAvailable() &&\r\n !this.validation.isSaving() &&\r\n this.store.isDirty() &&\r\n this.hasKey(),\r\n );\r\n\r\n protected readonly nodeCount = computed(() => dpeAllNodes(this.store.definition()).length);\r\n\r\n /** I nomi selezionati: la striscia contestuale compare da due in su. */\r\n protected readonly selectedNames = computed(() => this.store.selectedNodeNames());\r\n\r\n constructor() {\r\n // Caricamento: si reagisce all'**identita'** dell'input, non al suo contenuto. Lo store\r\n // diventa la fonte di verita' da qui in poi.\r\n effect(() => {\r\n const incoming = this.definition();\r\n // `untracked` non e' prudenza: senza, questo effect **si richiama da solo**. Legge il\r\n // documento dello store (attraverso `load`, e a maggior ragione se un domani si leggesse\r\n // qualcosa dello store qui) e lo riscrive, quindi ogni scrittura successiva — anche quella\r\n // del layout automatico — lo farebbe ripartire e riportare il documento all'input,\r\n // cancellando il layout appena calcolato e ricominciando. Il sintomo non e' un errore: e'\r\n // una pagina che non si ferma piu' e uno schermo che sembra solo lento.\r\n untracked(() => this.store.load(incoming));\r\n });\r\n\r\n // Un documento senza coordinate (ADR 0013: sono opzionali) si dispone da solo, una volta.\r\n // Farlo a ogni cambio combatterebbe con chi sposta i nodi a mano.\r\n effect(() => {\r\n const nodes = dpeAllNodes(this.store.definition()).map((located) => located.node);\r\n if (nodes.length === 0 || dpeHasCompleteLayout(nodes)) {\r\n return;\r\n }\r\n // Il calcolo legge il grafo e le misure delle card: fuori da `untracked` diventerebbero\r\n // dipendenze di questo effect, che scrive proprio cio' che legge.\r\n untracked(() => this.runLayout(true));\r\n });\r\n\r\n // La definizione esce verso l'ospite a ogni mutazione: e' lui che sa se e quando salvare.\r\n effect(() => {\r\n this.definitionChange.emit(this.store.definition());\r\n });\r\n }\r\n\r\n /** Comando «Riordina»: e' un'azione dell'utente, quindi annullabile e \"modifica\". */\r\n protected runAutoLayout(): void {\r\n this.runLayout(false);\r\n }\r\n\r\n /**\r\n * `isInitial` distingue due cose che dagre calcola allo stesso modo e che l'editor deve trattare\r\n * in modo opposto: disporre un documento che **non porta** coordinate non e' una modifica\r\n * dell'utente; riordinare su richiesta lo e'.\r\n */\r\n private runLayout(isInitial: boolean): void {\r\n const canvas = this.canvas();\r\n if (!canvas) {\r\n return;\r\n }\r\n const positions = this.layout.layout(\r\n canvas.layoutInput(),\r\n this.store.graph().edges.map((edge) => ({\r\n from: edge.fromNodeName,\r\n to: edge.toNodeName,\r\n id: edge.id,\r\n })),\r\n );\r\n if (isInitial) {\r\n this.store.applyInitialPositions(positions);\r\n return;\r\n }\r\n this.store.applyPositions(positions);\r\n }\r\n\r\n protected onNodeDropped(event: { collection: DpeNodeCollectionName; x: number; y: number }): void {\r\n const name = this.store.createNodeAt(event.collection, { x: event.x, y: event.y });\r\n // Selezione immediata: chi ha appena creato un nodo lo deve configurare, e cercarlo con il\r\n // mouse subito dopo averlo rilasciato e' un passaggio inutile.\r\n this.store.select(name);\r\n }\r\n\r\n /**\r\n * Doppio clic sul nodo, o bottone `✎` sulla card: i due gesti che il canvas emetteva su\r\n * `nodeOpened` e che **nessuno ascoltava** — il bottone non produceva nemmeno la selezione.\r\n *\r\n * Due cose, in quest'ordine. Si apre la dialog (che collassa la selezione a quel nodo) e si\r\n * **inquadra** il nodo: sotto il velo non si vede, ma alla chiusura si torna guardando il nodo su\r\n * cui si e' lavorato invece del punto dov'era il grafo prima.\r\n */\r\n protected onNodeOpened(name: string): void {\r\n this.store.openNodeEditor(name);\r\n this.canvas()?.revealNode(name);\r\n }\r\n\r\n /**\r\n * Elimina i nodi selezionati (striscia della selezione multipla). Si itera su una **copia** dei\r\n * nomi: `removeNode` pota la selezione a ogni giro, e scorrere il signal mentre lo si svuota\r\n * salterebbe la meta' dei nodi.\r\n */\r\n protected removeSelectedNodes(): void {\r\n for (const name of [...this.selectedNames()]) {\r\n this.store.removeNode(name);\r\n }\r\n }\r\n\r\n /** Clic sulla palette (via tastiera compresa): il nodo nasce in un punto libero e prevedibile. */\r\n protected onNodeRequested(collection: DpeNodeCollectionName): void {\r\n const existing = this.store.positions();\r\n let y = 60;\r\n for (const position of existing.values()) {\r\n y = Math.max(y, position.y + 140);\r\n }\r\n const name = this.store.createNodeAt(collection, { x: 80, y });\r\n this.store.select(name);\r\n }\r\n\r\n /**\r\n * `Ctrl+S` (e `Cmd+S`): stesse condizioni di abilitazione del bottone. `preventDefault` sempre\r\n * che la scorciatoia sia nostra, anche quando il salvataggio non e' possibile: lasciare passare\r\n * il «salva pagina» del browser proprio dentro un editor sarebbe la risposta sbagliata a un\r\n * gesto giusto.\r\n */\r\n protected onKeyDown(event: KeyboardEvent): void {\r\n if (!(event.ctrlKey || event.metaKey) || event.key.toLowerCase() !== 's') {\r\n return;\r\n }\r\n event.preventDefault();\r\n if (this.canSave()) {\r\n this.validation.save();\r\n }\r\n }\r\n\r\n /**\r\n * Navigazione da una diagnostica: **selezione e vista**. Il `DiagnosticPath` promette di portare\r\n * sul nodo e sul campo; selezionare senza inquadrare mantiene meta' della promessa, e il nodo\r\n * resta fuori schermo mentre l'ispettore parla di lui.\r\n */\r\n protected onNavigate(event: { nodeName: string }): void {\r\n this.store.select(event.nodeName);\r\n // Si muove la **vista**, non il documento: le coordinate del nodo non si toccano.\r\n this.canvas()?.revealNode(event.nodeName);\r\n }\r\n\r\n /**\r\n * Stato **sporco** del documento, per l'ospite che deve decidere se lasciar navigare via\r\n * (ADR 0019). Delega a `DpeDocumentStore.isDirty`.\r\n *\r\n * Si legge tramite `viewChild` **quando serve**, non si rispecchia in uno stato dell'ospite: un\r\n * mirror di stato sporco che va fuori sincrono e' peggio del problema che risolve. La conferma\r\n * prima di abbandonare le modifiche e' dell'ospite, perche' e' l'ospite che naviga — la libreria\r\n * non sa se andare via significhi cambiare schermata, scheda o applicazione.\r\n */\r\n readonly isDirty: Signal<boolean> = this.store.isDirty;\r\n\r\n /**\r\n * Da chiamare dopo un salvataggio riuscito: da quel momento il documento risulta \"senza\r\n * modifiche\". Lo decide l'ospite, perche' solo lui sa se il salvataggio e' andato a buon fine.\r\n */\r\n markSaved(): void {\r\n this.store.markSaved();\r\n }\r\n}\r\n","<!--\r\n `.dpe-builder` porta le variabili del tema: e’ la classe radice su cui l’app ospite le\r\n sovrascrive, e `data-dpe-theme=\"dark\"` sul contenitore ridefinisce i soli token.\r\n-->\r\n<div class=\"dpe-builder dpe-shell\">\r\n <header class=\"dpe-shell__bar\">\r\n <div class=\"dpe-shell__title\">\r\n <strong>{{ store.definition().label || store.definition().name || 'Definizione DPE' }}</strong>\r\n <span class=\"dpe-shell__sub\">{{ nodeCount() }} nodi</span>\r\n <!--\r\n Stato **sporco** rispetto all’ultimo salvataggio, non un conteggio di operazioni: serve a\r\n decidere se salvare, e un contatore che sale quando si annulla risponde alla domanda\r\n sbagliata. Annullare fino allo stato di partenza torna qui a «nessuna modifica».\r\n -->\r\n <span\r\n class=\"dpe-shell__dirty\"\r\n [class.dpe-shell__dirty--on]=\"store.isDirty()\"\r\n [title]=\"\r\n store.isDirty()\r\n ? 'Il documento differisce dall’ultimo salvataggio'\r\n : 'Il documento coincide con l’ultimo salvataggio'\r\n \"\r\n >\r\n @if (store.isDirty()) {\r\n ● modificata\r\n } @else {\r\n ○ nessuna modifica\r\n }\r\n </span>\r\n </div>\r\n\r\n <!--\r\n Badge in **sola lettura** (ADR 0017): il dialetto e’ dichiarato dall’ambiente, non scelto\r\n qui. Mostra quello della definizione, che e’ quello con cui il backend compilera’ (ADR\r\n 0009). Il titolo dice le due cose che contano — chi lo decide, e che il dialetto decide con\r\n quali capacita’ si compila. Non dice piu’ che le espressioni sono SQL di quel dialetto: da\r\n quando sono **formule** (ADR 0031) non lo sono, e il linguaggio si nomina accanto al campo.\r\n -->\r\n <span\r\n class=\"dpe-shell__dialect\"\r\n title=\"Il dialetto e’ dichiarato dall’ambiente e non si modifica dall’editor: decide con quali capacita’ il backend compilera’ questa definizione.\"\r\n >\r\n Dialetto · <strong>{{ dialectLabels[dialect()] }}</strong>\r\n </span>\r\n\r\n <div class=\"dpe-shell__group\">\r\n <button\r\n type=\"button\"\r\n class=\"dpe-shell__btn\"\r\n [disabled]=\"!store.canUndo()\"\r\n title=\"Annulla (la cronologia e’ una conseguenza dell’immutabilita’ dello stato)\"\r\n (click)=\"store.undo()\"\r\n >\r\n ↶ Annulla\r\n </button>\r\n <button\r\n type=\"button\"\r\n class=\"dpe-shell__btn\"\r\n [disabled]=\"!store.canRedo()\"\r\n title=\"Ripeti\"\r\n (click)=\"store.redo()\"\r\n >\r\n ↷ Ripeti\r\n </button>\r\n <button type=\"button\" class=\"dpe-shell__btn\" title=\"Riordina il grafo\" (click)=\"runAutoLayout()\">\r\n ⌗ Riordina\r\n </button>\r\n </div>\r\n\r\n <!--\r\n Salvataggio attraverso il contratto (`DpeBuilderApi.save`, opzionale). E’ il comando\r\n **primario**, separato dal gruppo annulla/ripeti/riordina: quelli disfano, questo scrive.\r\n\r\n **Sempre presente, mai nascosto.** Un bottone assente dice «questo editor non salva»; un\r\n bottone disabilitato con il motivo dice «qui non si salva, ed ecco perche’» — ed e’ la\r\n verita’ quando l’host non espone `save()`. La disponibilita’ si sa **prima del clic**\r\n (`core/dpe-capabilities.ts`), non dal primo fallimento.\r\n -->\r\n <div class=\"dpe-shell__group dpe-shell__group--primary\">\r\n <button\r\n type=\"button\"\r\n class=\"dpe-shell__btn dpe-shell__btn--primary\"\r\n [disabled]=\"!canSave()\"\r\n [title]=\"\r\n !validation.isSaveAvailable()\r\n ? 'Salvataggio non disponibile su questo ambiente: l’host non espone save()'\r\n : validation.isSaving()\r\n ? 'Salvataggio in corso'\r\n : !hasKey()\r\n ? 'Il documento non ha un nome: e’ la chiave del catalogo, e va creato da la’ (l’aggiornamento e’ PUT /definitions/{nome})'\r\n : store.isDirty()\r\n ? 'Salva la definizione (Ctrl+S)'\r\n : 'Nessuna modifica da salvare'\r\n \"\r\n (click)=\"validation.save()\"\r\n >\r\n @if (validation.isSaving()) {\r\n ⤓ Salvataggio…\r\n } @else {\r\n ⤓ Salva\r\n }\r\n </button>\r\n @if (validation.justSaved()) {\r\n <!-- Avviso transitorio: il fatto durevole e’ «nessuna modifica» nell’indicatore. -->\r\n <span class=\"dpe-shell__toast\" role=\"status\">✓ Salvata</span>\r\n }\r\n </div>\r\n\r\n <div class=\"dpe-shell__status\">\r\n @if (validation.isValidating()) {\r\n <span class=\"dpe-shell__pill dpe-shell__pill--busy\">Validazione in corso…</span>\r\n } @else if (validation.error(); as error) {\r\n <!--\r\n Un guasto di trasporto non cancella le diagnostiche precedenti: dire «va tutto bene»\r\n quando non lo sappiamo piu’ e’ peggio che mostrare un risultato vecchio dichiarandolo.\r\n -->\r\n <span class=\"dpe-shell__pill dpe-shell__pill--error\" [title]=\"error.message\">\r\n Backend non raggiungibile ({{ error.category }})\r\n </span>\r\n } @else if (store.validateResult()) {\r\n <span\r\n class=\"dpe-shell__pill\"\r\n [class.dpe-shell__pill--error]=\"store.validateResult()!.hasErrors\"\r\n [class.dpe-shell__pill--ok]=\"!store.validateResult()!.hasErrors\"\r\n >\r\n @if (store.validateResult()!.hasErrors) {\r\n Con errori\r\n } @else {\r\n Nessun errore\r\n }\r\n </span>\r\n }\r\n </div>\r\n </header>\r\n\r\n @if (dialectMismatch(); as declared) {\r\n <!--\r\n Disallineamento fra la dichiarazione dell’ambiente e la definizione (ADR 0017). Si nominano\r\n **entrambi** i dialetti e non si offre di correggere: cambiare dialetto cambia le capacita’\r\n con cui si compila e i dialetti che il compilatore di formule copre (ADR 0031 §5), ed e’\r\n dell’host. Non e’ una regola di dominio duplicata — non giudica la definizione, riporta il\r\n disaccordo fra due fatti che l’ospite ha fornito.\r\n -->\r\n <div class=\"dpe-shell__warn\" role=\"alert\">\r\n <strong>\r\n L’ambiente dichiara {{ dialectLabels[declared] }}, la definizione e’ scritta per\r\n {{ dialectLabels[dialect()] }}.\r\n </strong>\r\n <p>\r\n Il backend compilera’ con il dialetto <strong>della definizione</strong>. Allinearli cambia\r\n le capacita’ con cui si compila, e non si fa da qui.\r\n </p>\r\n </div>\r\n }\r\n\r\n <!--\r\n Il documento ricevuto non e' una definizione. Banner **persistente** e in cima: e' un difetto\r\n dell'integrazione, non dell'utente, e un canvas vuoto da solo non lo dice — sembra una\r\n definizione senza nodi. Non si nasconde l'editor: cio' che c'e' resta visibile, e il salvataggio\r\n e' disabilitato da se' perche' la chiave manca.\r\n -->\r\n @if (documentProblem(); as problem) {\r\n <div class=\"dpe-shell__warn dpe-shell__warn--error\" role=\"alert\">\r\n <strong>Il documento ricevuto non e’ una definizione: {{ problem }}.</strong>\r\n <p>\r\n L’editor riceve il documento dall’applicazione ospite, e questo non ha la forma di una\r\n <code>DpeDefinition</code>. Le tre cause reali: il backend restituisce l’involucro di lettura\r\n (<code>{{ '{' }} definition, diagnostics {{ '}' }}</code>) invece del documento; l’ospite\r\n passa il <strong>sommario</strong> del catalogo al posto della definizione caricata con\r\n <code>load(nome)</code>; la risposta e’ in PascalCase (<code>Name</code> non e’\r\n <code>name</code>).\r\n </p>\r\n </div>\r\n }\r\n\r\n @if (validation.saveError(); as error) {\r\n <!--\r\n Banner **persistente**: un salvataggio fallito in silenzio e’ peggio di nessun salvataggio,\r\n perche’ l’indicatore «modificata» sparirebbe per un’operazione che non e’ avvenuta. Qui non\r\n sparisce: `markSaved()` viene chiamato solo al successo.\r\n -->\r\n <div class=\"dpe-shell__warn dpe-shell__warn--error\" role=\"alert\">\r\n <strong>Salvataggio non riuscito ({{ error.category }})</strong>\r\n <p>{{ error.message }}</p>\r\n <div class=\"dpe-shell__group\">\r\n <button type=\"button\" class=\"dpe-shell__btn\" (click)=\"validation.dismissSaveError()\">\r\n Ho capito\r\n </button>\r\n </div>\r\n </div>\r\n }\r\n\r\n @if (selectedNames().length > 1) {\r\n <!--\r\n Striscia contestuale della **selezione multipla**. Sta qui, fra i banner di documento e il\r\n corpo, perche' e' un fatto che riguarda l’insieme e non un nodo — ed e' dove l’occhio guarda\r\n per i fatti dell’insieme. Non mostra i campi di un nodo: mostrarli mentre N sono evidenziati\r\n e' la stessa bugia della doppia evidenziazione, detta piu' piano.\r\n -->\r\n <div class=\"dpe-shell__multi\">\r\n <div class=\"dpe-shell__multi-head\">\r\n <strong>{{ selectedNames().length }} nodi selezionati</strong>\r\n <ul class=\"dpe-multi__names\">\r\n @for (name of selectedNames(); track $index) {\r\n <li class=\"dpe-multi__name\">{{ name }}</li>\r\n }\r\n </ul>\r\n </div>\r\n\r\n <div class=\"dpe-shell__group\">\r\n <button type=\"button\" class=\"dpe-shell__btn\" (click)=\"removeSelectedNodes()\">\r\n Elimina i {{ selectedNames().length }} nodi\r\n </button>\r\n <!--\r\n Il riordino e’ dichiaratamente **dell’intero grafo**: dagre su un sottografo sarebbe una\r\n funzione nuova, e chiamarla «riordina i selezionati» mentre riordina tutto sarebbe una\r\n bugia — la stessa classe di difetto che questa striscia esiste per evitare.\r\n -->\r\n <button type=\"button\" class=\"dpe-shell__btn\" (click)=\"runAutoLayout()\">\r\n Riordina il grafo\r\n </button>\r\n </div>\r\n\r\n <p class=\"dpe-note dpe-note--warning\">\r\n L’eliminazione lascia i riferimenti a valle così come sono: diventano riferimenti rotti, e\r\n il backend li segnala. Si annulla con ↶.\r\n </p>\r\n </div>\r\n }\r\n\r\n <div class=\"dpe-shell__body\">\r\n <dpe-node-palette class=\"dpe-shell__palette\" (nodeRequested)=\"onNodeRequested($event)\" />\r\n\r\n <dpe-canvas\r\n class=\"dpe-shell__canvas\"\r\n [isEditable]=\"isEditable()\"\r\n (nodeDropped)=\"onNodeDropped($event)\"\r\n (nodeOpened)=\"onNodeOpened($event)\"\r\n />\r\n </div>\r\n\r\n <!--\r\n **Una sola istanza**, fratello di `.dpe-shell__body`: non dentro il canvas (finirebbe nella\r\n trasformazione CSS di `f-canvas`) e non dentro il corpo (partecipererebbe alla griglia). Non\r\n partecipa al layout: `showModal()` la promuove nel top layer, quindi aprirla non produce\r\n nessun reflow del canvas e non c'e' nessuna inquadratura da rifare.\r\n -->\r\n <dpe-node-dialog />\r\n\r\n <div class=\"dpe-shell__bottom\">\r\n <div class=\"dpe-shell__tabs\" role=\"tablist\" aria-label=\"Pannelli\">\r\n <button\r\n type=\"button\"\r\n role=\"tab\"\r\n class=\"dpe-shell__tab\"\r\n [class.dpe-shell__tab--active]=\"bottomTab() === 'diagnostics'\"\r\n [attr.aria-selected]=\"bottomTab() === 'diagnostics'\"\r\n (click)=\"bottomTab.set('diagnostics')\"\r\n >\r\n Diagnostiche\r\n </button>\r\n <button\r\n type=\"button\"\r\n role=\"tab\"\r\n class=\"dpe-shell__tab\"\r\n [class.dpe-shell__tab--active]=\"bottomTab() === 'schema'\"\r\n [attr.aria-selected]=\"bottomTab() === 'schema'\"\r\n (click)=\"bottomTab.set('schema')\"\r\n >\r\n Schema\r\n </button>\r\n <button\r\n type=\"button\"\r\n role=\"tab\"\r\n class=\"dpe-shell__tab\"\r\n [class.dpe-shell__tab--active]=\"bottomTab() === 'sql'\"\r\n [attr.aria-selected]=\"bottomTab() === 'sql'\"\r\n (click)=\"bottomTab.set('sql')\"\r\n >\r\n SQL generato\r\n </button>\r\n </div>\r\n\r\n @switch (bottomTab()) {\r\n @case ('diagnostics') {\r\n <dpe-diagnostics-panel class=\"dpe-shell__panel\" (navigate)=\"onNavigate($event)\" />\r\n }\r\n @case ('schema') {\r\n <dpe-schema-panel class=\"dpe-shell__panel\" />\r\n }\r\n @case ('sql') {\r\n <dpe-sql-preview class=\"dpe-shell__panel\" />\r\n }\r\n }\r\n </div>\r\n</div>\r\n","/**\r\n * Il catalogo delle definizioni: `<dpe-catalog>` (ADR 0019).\r\n *\r\n * **E' fratello dell'editor, non suo genitore.** `DpeBuilderComponent` non lo importa, non lo\r\n * conosce e non lo monta: `definition` resta `input.required`, l'ospite che vuole solo l'editor\r\n * non paga nulla, e due editor nella stessa pagina non diventano due cataloghi affiancati.\r\n *\r\n * Cosa fa: elenca, filtra in locale, crea, duplica, cancella. Cosa **non** fa: aprire. Emette il\r\n * **nome** con `open` e si ferma li', perche' chi apre puo' voler instradare, controllare permessi\r\n * o aprire in un'altra scheda — decisioni dell'ospite, non nostre.\r\n *\r\n * Provider dello store a livello di **componente**, come per l'editor: due cataloghi in due punti\r\n * dell'ospite sono due elenchi indipendenti.\r\n */\r\n\r\nimport {\r\n ChangeDetectionStrategy,\r\n Component,\r\n ElementRef,\r\n Injector,\r\n afterNextRender,\r\n computed,\r\n inject,\r\n output,\r\n signal,\r\n viewChild,\r\n} from '@angular/core';\r\n\r\nimport { DpeCatalogStore } from '../../core/dpe-catalog.store';\r\nimport { DPE_DIALECT_LABELS } from '../../core/dpe-node-catalog';\r\nimport { DpeDefinitionSummary } from '../../model/dpe-api.model';\r\n\r\n@Component({\r\n selector: 'dpe-catalog',\r\n standalone: true,\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n providers: [DpeCatalogStore],\r\n templateUrl: './dpe-catalog.component.html',\r\n})\r\nexport class DpeCatalogComponent {\r\n protected readonly store = inject(DpeCatalogStore);\r\n private readonly injector = inject(Injector);\r\n\r\n /**\r\n * Il **nome** della definizione da aprire. Non la definizione: caricarla e' dell'ospite, che\r\n * potrebbe volerlo fare per vie proprie.\r\n */\r\n readonly open = output<string>();\r\n\r\n protected readonly dialectLabels = DPE_DIALECT_LABELS;\r\n\r\n /** Filtro **locale** sui sommari gia' ricevuti: non e' una query al backend (ADR 0019). */\r\n protected readonly filter = signal('');\r\n\r\n protected readonly newName = signal('');\r\n protected readonly newLabel = signal('');\r\n\r\n /**\r\n * Conferma in **due passi**: un elenco in cui un clic distratto cancella non e' un elenco.\r\n * Tiene il nome invece di un booleano perche' la conferma appartiene a **quella** riga.\r\n */\r\n protected readonly pendingDelete = signal<string | null>(null);\r\n\r\n /** Riga in duplicazione, con i due campi del nome e dell'etichetta nuovi. */\r\n protected readonly duplicateSource = signal<string | null>(null);\r\n protected readonly duplicateName = signal('');\r\n protected readonly duplicateLabel = signal('');\r\n\r\n private readonly duplicateNameField =\r\n viewChild<ElementRef<HTMLInputElement>>('duplicateNameField');\r\n\r\n /**\r\n * Il filtro non tocca l'ordine: si tolgono righe, non si riordinano. `toLocaleLowerCase` e non\r\n * `toLowerCase` perche' un elenco di definizioni italiane si filtra anche per lettere accentate.\r\n */\r\n protected readonly visible = computed<readonly DpeDefinitionSummary[]>(() => {\r\n const needle = this.filter().trim().toLocaleLowerCase();\r\n if (needle.length === 0) {\r\n return this.store.summaries();\r\n }\r\n return this.store\r\n .summaries()\r\n .filter(\r\n (summary) =>\r\n summary.name.toLocaleLowerCase().includes(needle) ||\r\n summary.label.toLocaleLowerCase().includes(needle),\r\n );\r\n });\r\n\r\n /** Vero quando il filtro ha nascosto tutto: e' un messaggio diverso da «nessuna definizione». */\r\n protected readonly isFilteredEmpty = computed(\r\n () => this.store.summaries().length > 0 && this.visible().length === 0,\r\n );\r\n\r\n /**\r\n * Duplicare richiede **due** primitive, `load` e `createDefinition`: non esiste una primitiva\r\n * di duplicazione, il comando si compone da queste.\r\n */\r\n protected readonly canDuplicate = computed(\r\n () => this.store.capabilities.load && this.store.capabilities.createDefinition,\r\n );\r\n\r\n protected readonly duplicateReason = computed(() =>\r\n this.canDuplicate()\r\n ? 'Duplica: carica questa definizione e la ricrea con un nome nuovo'\r\n : 'Duplicazione non disponibile: questo ambiente non implementa load e createDefinition.',\r\n );\r\n\r\n /** Validazione **di forma**, l'unica ammessa nel frontend: due campi non vuoti. */\r\n protected readonly canSubmitNew = computed(\r\n () =>\r\n this.store.createCommand.isAvailable &&\r\n !this.store.isMutating() &&\r\n this.newName().trim().length > 0 &&\r\n this.newLabel().trim().length > 0,\r\n );\r\n\r\n protected readonly canSubmitDuplicate = computed(\r\n () =>\r\n this.canDuplicate() &&\r\n !this.store.isMutating() &&\r\n this.duplicateName().trim().length > 0 &&\r\n this.duplicateLabel().trim().length > 0,\r\n );\r\n\r\n protected onOpen(name: string): void {\r\n this.open.emit(name);\r\n }\r\n\r\n protected refresh(): void {\r\n this.pendingDelete.set(null);\r\n this.store.refresh();\r\n }\r\n\r\n /**\r\n * Crea e **apre il nome tornato dall'ospite**, non quello digitato: l'ospite puo' averlo\r\n * normalizzato, e aprire quello digitato aprirebbe una definizione che non esiste.\r\n */\r\n protected createNew(): void {\r\n if (!this.canSubmitNew()) {\r\n return;\r\n }\r\n const dialect = this.store.environmentDialect();\r\n if (dialect === undefined) {\r\n // Non dovrebbe accadere: `createCommand` lo esige. Se accade, non si ripiega su un\r\n // dialetto qualsiasi — sarebbe una definizione per il database sbagliato.\r\n return;\r\n }\r\n this.store\r\n .create(this.newName().trim(), this.newLabel().trim(), dialect)\r\n .subscribe((summary) => {\r\n this.newName.set('');\r\n this.newLabel.set('');\r\n this.open.emit(summary.name);\r\n });\r\n }\r\n\r\n protected startDuplicate(summary: DpeDefinitionSummary): void {\r\n this.pendingDelete.set(null);\r\n this.duplicateSource.set(summary.name);\r\n this.duplicateName.set(`${summary.name}Copia`);\r\n this.duplicateLabel.set(`${summary.label} (copia)`);\r\n // `afterNextRender` e non `setTimeout`: il rinvio al task successivo scatta **prima** del\r\n // refresh della view, la query e' ancora vuota e il focus non arriva — senza nessun errore.\r\n afterNextRender(() => this.duplicateNameField()?.nativeElement.focus(), {\r\n injector: this.injector,\r\n });\r\n }\r\n\r\n protected cancelDuplicate(): void {\r\n this.duplicateSource.set(null);\r\n }\r\n\r\n protected confirmDuplicate(): void {\r\n const source = this.duplicateSource();\r\n if (source === null || !this.canSubmitDuplicate()) {\r\n return;\r\n }\r\n this.store\r\n .duplicate(source, this.duplicateName().trim(), this.duplicateLabel().trim())\r\n .subscribe(() => {\r\n // Si resta nell'elenco: la riga nuova che compare **e'** la conferma. Aprire la copia\r\n // porterebbe via dall'elenco proprio chi sta riorganizzando il catalogo.\r\n this.duplicateSource.set(null);\r\n });\r\n }\r\n\r\n protected askDelete(name: string): void {\r\n this.duplicateSource.set(null);\r\n this.pendingDelete.set(name);\r\n }\r\n\r\n protected cancelDelete(): void {\r\n this.pendingDelete.set(null);\r\n }\r\n\r\n protected confirmDelete(name: string): void {\r\n this.pendingDelete.set(null);\r\n this.store.remove(name);\r\n }\r\n\r\n /** Solo la data, senza ora: un elenco non e' un registro di audit. */\r\n protected formatDate(value: string): string {\r\n const parsed = new Date(value);\r\n return Number.isNaN(parsed.getTime()) ? value : parsed.toLocaleDateString();\r\n }\r\n}\r\n","<!--\r\n `.dpe-theme` porta le variabili del tema anche fuori dall’editor: il catalogo e’ un componente\r\n **fratello**, quindi non puo’ contare sulla classe radice di `<dpe-builder>`. La variante scura\r\n resta quella prevista, `data-dpe-theme=\"dark\"` su un contenitore dell’ospite.\r\n-->\r\n<section class=\"dpe-theme dpe-cat\" aria-labelledby=\"dpe-cat-title\">\r\n <header class=\"dpe-cat__bar\">\r\n <h2 class=\"dpe-cat__title\" id=\"dpe-cat-title\">Definizioni DPE</h2>\r\n\r\n <span class=\"dpe-cat__count\">\r\n @if (store.isLoading()) {\r\n caricamento…\r\n } @else if (!store.capabilities.listDefinitions) {\r\n elenco non disponibile\r\n } @else {\r\n {{ visible().length }} di {{ store.summaries().length }}\r\n }\r\n </span>\r\n\r\n <input\r\n class=\"dpe-cat__filter\"\r\n type=\"search\"\r\n placeholder=\"Filtra per nome o etichetta\"\r\n aria-label=\"Filtra le definizioni\"\r\n [value]=\"filter()\"\r\n (input)=\"filter.set($any($event.target).value)\"\r\n />\r\n\r\n <button\r\n type=\"button\"\r\n class=\"dpe-cat__btn\"\r\n [disabled]=\"!store.capabilities.listDefinitions || store.isLoading()\"\r\n [title]=\"\r\n store.capabilities.listDefinitions\r\n ? 'Ricarica l’elenco'\r\n : 'Questo ambiente non implementa listDefinitions.'\r\n \"\r\n (click)=\"refresh()\"\r\n >\r\n ⟳ Aggiorna\r\n </button>\r\n </header>\r\n\r\n @if (store.error(); as error) {\r\n <!--\r\n Banner persistente: un errore di catalogo che svanisce lascia un elenco vecchio senza dire\r\n che e’ vecchio. La categoria si mostra perche’ distingue «riprova» da «chiedi all’ospite».\r\n -->\r\n <p class=\"dpe-cat__error\" role=\"alert\">\r\n <strong>{{ error.category }}</strong> {{ error.message }}\r\n <button type=\"button\" class=\"dpe-cat__dismiss\" aria-label=\"Chiudi\" (click)=\"store.dismissError()\">\r\n ×\r\n </button>\r\n </p>\r\n }\r\n\r\n <ul class=\"dpe-cat__list\">\r\n @for (summary of visible(); track summary.name) {\r\n <li class=\"dpe-cat__item\" [class.dpe-cat__item--busy]=\"store.isMutating()\">\r\n <div class=\"dpe-cat__row\">\r\n <button\r\n type=\"button\"\r\n class=\"dpe-cat__open\"\r\n [title]=\"'Apri ' + summary.label\"\r\n (click)=\"onOpen(summary.name)\"\r\n >\r\n <span class=\"dpe-cat__label\">{{ summary.label }}</span>\r\n <span class=\"dpe-cat__meta\">\r\n <!--\r\n Solo i fatti **presenti**: cio’ che il sommario non porta non si stima. Contare i\r\n nodi lato client vorrebbe dire caricare tutte le definizioni per disegnare un\r\n elenco (ADR 0019).\r\n -->\r\n <code class=\"dpe-cat__name\">{{ summary.name }}</code>\r\n <span class=\"dpe-cat__dot\"> · </span>\r\n {{ dialectLabels[summary.dialect] }}\r\n @if (summary.status) {\r\n <span class=\"dpe-cat__dot\"> · </span>\r\n {{ summary.status === 'Active' ? 'attiva' : 'non attiva' }}\r\n }\r\n @if (summary.runMode) {\r\n <span class=\"dpe-cat__dot\"> · </span>\r\n {{ summary.runMode === 'Batch' ? 'batch' : 'a richiesta' }}\r\n }\r\n @if (summary.isTemplate) {\r\n <span class=\"dpe-cat__dot\"> · </span>\r\n modello\r\n }\r\n @if (summary.nodeCount !== undefined) {\r\n <span class=\"dpe-cat__dot\"> · </span>\r\n {{ summary.nodeCount }} {{ summary.nodeCount === 1 ? 'nodo' : 'nodi' }}\r\n }\r\n @if (summary.updatedAt) {\r\n <span class=\"dpe-cat__dot\"> · </span>\r\n {{ formatDate(summary.updatedAt) }}\r\n @if (summary.updatedBy) {\r\n · {{ summary.updatedBy }}\r\n }\r\n }\r\n </span>\r\n @if (summary.description) {\r\n <span class=\"dpe-cat__desc\">{{ summary.description }}</span>\r\n }\r\n </button>\r\n\r\n <!--\r\n `hasErrors` e’ **riportato, non ricalcolato** (ADR 0019): il frontend non valida\r\n niente. Per questo l’etichetta dice «ultima validazione nota» e non «invalida»: puo’\r\n essere vecchia, e spacciarla per verita’ sarebbe una validazione fatta dal client per\r\n interposta persona. Assente, non compare nulla.\r\n -->\r\n @if (summary.hasErrors !== undefined) {\r\n <span\r\n class=\"dpe-cat__flag\"\r\n [class.dpe-cat__flag--errors]=\"summary.hasErrors\"\r\n [title]=\"\r\n summary.hasErrors\r\n ? 'Ultima validazione nota all’ambiente: con errori. E’ uno stato riportato, non ricalcolato qui.'\r\n : 'Ultima validazione nota all’ambiente: senza errori. E’ uno stato riportato, non ricalcolato qui.'\r\n \"\r\n >\r\n {{ summary.hasErrors ? 'con errori (stato noto)' : 'senza errori (stato noto)' }}\r\n </span>\r\n }\r\n\r\n <div class=\"dpe-cat__actions\">\r\n <button\r\n type=\"button\"\r\n class=\"dpe-cat__btn\"\r\n [disabled]=\"!canDuplicate() || store.isMutating()\"\r\n [title]=\"duplicateReason()\"\r\n (click)=\"startDuplicate(summary)\"\r\n >\r\n Duplica\r\n </button>\r\n\r\n @if (pendingDelete() === summary.name) {\r\n <!-- Conferma in **due passi**: la prima pressione chiede, la seconda cancella. -->\r\n <span class=\"dpe-cat__confirm\">\r\n Eliminare «{{ summary.name }}»?\r\n <button\r\n type=\"button\"\r\n class=\"dpe-cat__btn dpe-cat__btn--danger\"\r\n (click)=\"confirmDelete(summary.name)\"\r\n >\r\n Elimina\r\n </button>\r\n <button type=\"button\" class=\"dpe-cat__btn\" (click)=\"cancelDelete()\">Annulla</button>\r\n </span>\r\n } @else {\r\n <button\r\n type=\"button\"\r\n class=\"dpe-cat__btn\"\r\n [disabled]=\"!store.capabilities.deleteDefinition || store.isMutating()\"\r\n [title]=\"\r\n store.capabilities.deleteDefinition\r\n ? 'Elimina (chiede conferma)'\r\n : 'Eliminazione non disponibile: questo ambiente non implementa deleteDefinition.'\r\n \"\r\n (click)=\"askDelete(summary.name)\"\r\n >\r\n Elimina\r\n </button>\r\n }\r\n </div>\r\n </div>\r\n\r\n @if (duplicateSource() === summary.name) {\r\n <!--\r\n Duplicare e’ `load` piu’ `createDefinition`: nessuna primitiva nuova sul contratto.\r\n Il dialetto resta quello della definizione copiata: cambiarlo copiando sarebbe una\r\n migrazione silenziosa delle capacita’ con cui la copia verra’ compilata.\r\n -->\r\n <div class=\"dpe-cat__form dpe-cat__form--inline\">\r\n <label class=\"dpe-cat__field\">\r\n <span>Nome della copia</span>\r\n <input\r\n #duplicateNameField\r\n class=\"dpe-cat__input\"\r\n [value]=\"duplicateName()\"\r\n (input)=\"duplicateName.set($any($event.target).value)\"\r\n />\r\n </label>\r\n <label class=\"dpe-cat__field\">\r\n <span>Etichetta della copia</span>\r\n <input\r\n class=\"dpe-cat__input\"\r\n [value]=\"duplicateLabel()\"\r\n (input)=\"duplicateLabel.set($any($event.target).value)\"\r\n />\r\n </label>\r\n <button\r\n type=\"button\"\r\n class=\"dpe-cat__btn dpe-cat__btn--primary\"\r\n [disabled]=\"!canSubmitDuplicate()\"\r\n (click)=\"confirmDuplicate()\"\r\n >\r\n Duplica\r\n </button>\r\n <button type=\"button\" class=\"dpe-cat__btn\" (click)=\"cancelDuplicate()\">Annulla</button>\r\n </div>\r\n }\r\n </li>\r\n } @empty {\r\n <li class=\"dpe-cat__empty\">\r\n @if (!store.capabilities.listDefinitions) {\r\n Questo ambiente non implementa <code>listDefinitions</code>: l’elenco lo fornisce\r\n l’applicazione ospite.\r\n } @else if (store.isLoading()) {\r\n Caricamento dell’elenco…\r\n } @else if (isFilteredEmpty()) {\r\n Nessuna definizione corrisponde al filtro.\r\n } @else if (store.hasLoaded()) {\r\n Nessuna definizione. Creane una qui sotto.\r\n } @else {\r\n Elenco non disponibile.\r\n }\r\n </li>\r\n }\r\n </ul>\r\n\r\n <footer class=\"dpe-cat__foot\">\r\n <div class=\"dpe-cat__form\">\r\n <label class=\"dpe-cat__field\">\r\n <span>Nome tecnico</span>\r\n <input\r\n class=\"dpe-cat__input\"\r\n placeholder=\"RiepilogoOrdini\"\r\n [value]=\"newName()\"\r\n [disabled]=\"!store.createCommand.isAvailable\"\r\n (input)=\"newName.set($any($event.target).value)\"\r\n />\r\n </label>\r\n <label class=\"dpe-cat__field\">\r\n <!--\r\n Si chiedono **due** campi e non uno: passare il nome anche come etichetta produce per\r\n sempre un catalogo di nomi tecnici, e un default sbagliato non si corregge piu’.\r\n -->\r\n <span>Etichetta</span>\r\n <input\r\n class=\"dpe-cat__input\"\r\n placeholder=\"Riepilogo ordini\"\r\n [value]=\"newLabel()\"\r\n [disabled]=\"!store.createCommand.isAvailable\"\r\n (input)=\"newLabel.set($any($event.target).value)\"\r\n />\r\n </label>\r\n <button\r\n type=\"button\"\r\n class=\"dpe-cat__btn dpe-cat__btn--primary\"\r\n [disabled]=\"!canSubmitNew()\"\r\n [title]=\"\r\n store.createCommand.reason ??\r\n 'Crea la definizione e la apre. Il dialetto viene dall’ambiente, non si sceglie qui.'\r\n \"\r\n (click)=\"createNew()\"\r\n >\r\n + Crea e apri\r\n </button>\r\n </div>\r\n\r\n <p class=\"dpe-cat__hint\">\r\n @if (store.createCommand.isAvailable) {\r\n <!--\r\n Detto **al momento della creazione**, non lasciato scoprire: una definizione nuova non\r\n ha sorgenti ne’ writeback, quindi l’editor la segnalera’ invalida. E’ normale — il\r\n modello tollera l’incompleto e la validazione lo racconta.\r\n -->\r\n Il dialetto della definizione nuova e’ quello dichiarato dall’ambiente\r\n @if (store.environmentDialect(); as declared) {\r\n (<strong>{{ dialectLabels[declared] }}</strong>)\r\n }\r\n e non si sceglie qui. Una definizione appena creata\r\n e’ <strong>invalida</strong> — servono almeno una sorgente e un writeback — ed e’ normale.\r\n } @else {\r\n {{ store.createCommand.reason }}\r\n }\r\n </p>\r\n </footer>\r\n</section>\r\n","/*\r\n * Superficie pubblica di `@esfaenza/dpe-builder`.\r\n *\r\n * Cio' che non e' esportato qui non e' API: chi integra la libreria usa questi tipi e nulla\r\n * di piu'. In questa tranche esistono contratto, modello e `core/`; i componenti dell'editor\r\n * arrivano nella successiva.\r\n */\r\n\r\n// Contratto verso il backend: il contratto **e'** la classe astratta (ADR 0007).\r\nexport * from './lib/api/dpe-builder-api';\r\nexport * from './lib/api/dpe-api-error';\r\n// Client HTTP di libreria (ADR 0020, che revisiona la collocazione decisa dall'ADR 0007). E'\r\n// **una** implementazione del contratto, non il contratto: chi non usa HTTP non la registra e\r\n// `sideEffects: false` la rende eliminabile dal bundle.\r\nexport * from './lib/api/http-dpe-builder-api';\r\n\r\n// Modello: allineato a mano ai record C#.\r\nexport * from './lib/model/dpe-definition.model';\r\nexport * from './lib/model/dpe-diagnostics.model';\r\nexport * from './lib/model/dpe-api.model';\r\n\r\n// Stato e utility pure.\r\nexport * from './lib/core/dpe-capabilities';\r\nexport * from './lib/core/dpe-catalog.store';\r\n// Le condizioni dei campi condizionali, in un punto solo: le legge la resa di §5-ter e le legge la\r\n// ripulitura. Esportate perche' chi scrive un ispettore suo deve leggere **queste**, non ricopiarle.\r\nexport * from './lib/core/dpe-conditional-fields';\r\nexport * from './lib/core/dpe-document.store';\r\n// La cache del catalogo delle formule (ADR 0036, `frontend.md` §5-quater): esportata perche' chi\r\n// scrive un ispettore suo deve passare da qui e non chiamare `getFormulaCatalog` per conto proprio.\r\nexport * from './lib/core/dpe-formula-catalog.store';\r\n// La lettura dei tre esiti del catalogo: pura, e la stessa per chiunque renda un campo formula.\r\n// Riscriverla in un ispettore dell'ospite e' il modo di far tornare il degrado silenzioso.\r\nexport * from './lib/core/dpe-formula-catalog.view';\r\nexport * from './lib/core/dpe-diagnostics.util';\r\nexport * from './lib/core/dpe-graph';\r\nexport * from './lib/core/dpe-layout.service';\r\nexport * from './lib/core/dpe-metadata.store';\r\nexport * from './lib/core/dpe-node-catalog';\r\nexport * from './lib/core/dpe-node-icons';\r\nexport * from './lib/core/dpe-node-factory';\r\nexport * from './lib/core/dpe-node-layout';\r\nexport * from './lib/core/dpe-validation.service';\r\nexport * from './lib/core/dpe-viewport';\r\n\r\n// UI: il componente radice e i pezzi montabili da soli.\r\nexport * from './lib/ui/dpe-builder.component';\r\nexport * from './lib/ui/canvas/dpe-canvas.component';\r\n// Il catalogo (ADR 0019) e' **fratello** dell'editor: esportato perche' l'ospite possa montarlo,\r\n// e non importato da `DpeBuilderComponent`. Chi vuole solo l'editor non lo paga.\r\nexport * from './lib/ui/catalog/dpe-catalog.component';\r\nexport * from './lib/ui/canvas/dpe-canvas.model';\r\nexport * from './lib/ui/palette/dpe-node-palette.component';\r\n// La dialog di modifica del nodo (ADR 0026) e i due componenti che ne riempiono le colonne.\r\n// `dpe-node-inspector` resta esportato ma **cambia forma**: solo i campi modificabili di un nodo.\r\nexport * from './lib/ui/dialog/dpe-node-dialog.component';\r\nexport * from './lib/ui/inspector/dpe-node-inspector.component';\r\nexport * from './lib/ui/inspector/dpe-node-facts.component';\r\nexport * from './lib/ui/inspector/dpe-node-inspector-base';\r\nexport * from './lib/ui/diagnostics/dpe-diagnostics-panel.component';\r\nexport * from './lib/ui/schema/dpe-schema-panel.component';\r\nexport * from './lib/ui/preview/dpe-sql-preview.component';\r\nexport * from './lib/ui/shared/dpe-expression-editor.component';\r\nexport * from './lib/ui/shared/dpe-column-type-editor.component';\r\nexport * from './lib/ui/shared/dpe-select-value.directive';\r\nexport * from './lib/ui/shared/dpe-node-icon.component';\r\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":["FUNCTIONS","KINDS","DELIMITERS"],"mappings":"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"}
|