@gabrielerandelli/minus-tracker 0.8.0 → 0.8.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -2,6 +2,21 @@
2
2
 
3
3
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](./LICENSE)
4
4
  [![npm version](https://img.shields.io/npm/v/@gabrielerandelli/minus-tracker.svg)](https://www.npmjs.com/package/@gabrielerandelli/minus-tracker)
5
+ [![npm downloads](https://img.shields.io/npm/dm/@gabrielerandelli/minus-tracker.svg)](https://www.npmjs.com/package/@gabrielerandelli/minus-tracker)
6
+ [![Node](https://img.shields.io/badge/node-%3E%3D24-brightgreen.svg)](https://nodejs.org)
7
+ [![MCP](https://img.shields.io/badge/MCP-server-blueviolet.svg)](https://modelcontextprotocol.io)
8
+
9
+ ⚠️ **Ausilio al calcolo, non consulenza fiscale** — vedi l'[Avvertenza](#avvertenza).
10
+ ⚠️ **Calculation aid only, not tax advice** — see the [Disclaimer](#disclaimer).
11
+
12
+ 🤖 **Novità: server MCP** — qualsiasi agente AI può calcolare plusvalenze/minusvalenze italiane direttamente, senza passare dalla CLI.
13
+ 🤖 **New: MCP server** — any AI agent can compute Italian capital gains/losses directly, no CLI required.
14
+
15
+ ✅ Validato su tutti i 12 scenari FAQ dell'Agenzia delle Entrate · 120 test automatici · zero dipendenze runtime nella libreria core.
16
+ ✅ Validated against all 12 Agenzia delle Entrate FAQ scenarios · 120 automated tests · zero runtime dependencies in the core library.
17
+
18
+ _L'unico tool open-source che trasforma un export DEGIRO grezzo direttamente in plusvalenze/minusvalenze corrette secondo l'Agenzia delle Entrate — come CLI, libreria, o strumento MCP richiamabile dal tuo agente AI._
19
+ _The only open-source tool that turns a raw DEGIRO export directly into Agenzia-Entrate-correct capital gains/losses — as a CLI, a library, or an MCP tool your AI agent can call._
5
20
 
6
21
  ---
7
22
 
@@ -16,8 +31,9 @@ Il tool elabora i dati partendo direttamente dal formato CSV esportato da DEGIRO
16
31
  - Calcolo di plusvalenze e minusvalenze con gestione dei lotti via **LIFO o FIFO** (configurabile)
17
32
  - **Parser integrato** per i file CSV di DEGIRO
18
33
  - Gestione **multivaluta** con tassi storici BCE (EUR, USD, GBP, CHF)
19
- - **Classificazione a due categorie** (Bucket A/B) degli strumenti finanziari — azioni, ETF, titoli
20
- di stato, derivati — con classificazione automatica via OpenFIGI e sidecar JSON persistente
34
+ - **Classificazione fiscale degli strumenti finanziari in due categorie** (Bucket A/B) — azioni,
35
+ ETF, titoli di stato, derivati — con classificazione automatica tramite OpenFIGI e sidecar JSON
36
+ persistente
21
37
  - **Modello Redditi PF**: genera Quadro RT (redditi diversi) e Quadro RM (redditi di capitale) a
22
38
  partire dai lotti calcolati, con riporto delle minusvalenze pregresse (regola dei 4 anni)
23
39
  - Suite di test allineata alle **FAQ dell'Agenzia delle Entrate**
@@ -27,7 +43,7 @@ Il tool elabora i dati partendo direttamente dal formato CSV esportato da DEGIRO
27
43
  **Novità in v0.8.0:**
28
44
 
29
45
  - **Modalità stateless per `Classifier`**: `classify()` può ora funzionare senza alcun accesso al
30
- filesystem (nessun sidecar), utile per l'integrazione in agenti/automazioni
46
+ filesystem (nessun sidecar), utile per l'integrazione con agenti e automazioni
31
47
  - **Server MCP** (`minus-tracker-mcp`): espone `parse_transactions`, `classify_instruments` e
32
48
  `calculate_gains` come tool MCP su stdio, per l'uso diretto da parte di agenti AI — vedi la
33
49
  sezione [Server MCP](#server-mcp) più sotto
@@ -97,16 +113,16 @@ npx @gabrielerandelli/minus-tracker calc trades.csv
97
113
 
98
114
  ### Utilizzo CLI
99
115
 
100
- | Comando | Flag principali | Note |
101
- | ---------------------- | -------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------- |
102
- | `calc <file.csv>` | `--method LIFO\|FIFO` (default: LIFO), `--lang it\|en`, `--json`, `--export-dichiarazione [path]`, `--carry-forward` | Aggiorna i tassi BCE automaticamente se la snapshot ha più di 7 giorni; usa il sidecar di `classify` se presente |
103
- | `classify <file.csv>` | `--offline` | Classifica gli strumenti (Bucket A/B) e crea/aggiorna il sidecar `*.classify.json` |
104
- | `validate <file.csv>` | `--lang it\|en` | Exit 0 con avvisi; exit 1 in caso di errori bloccanti |
105
- | `rates --check` | — | Mostra la copertura della snapshot BCE in locale |
106
- | `rates --update` | — | Scarica i tassi aggiornati dall'API BCE |
107
- | `config --lang it\|en` | — | Salva la lingua preferita |
108
- | `config --show` | — | Mostra la lingua correntemente impostata |
109
- | `stress-test` | `--range N-M`, `--keep`, `--json`, `--output-dir` | Documentato in fondo |
116
+ | Comando | Flag principali | Note |
117
+ | ---------------------- | -------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------- |
118
+ | `calc <file.csv>` | `--method LIFO\|FIFO` (default: LIFO), `--lang it\|en`, `--json`, `--export-dichiarazione [path]`, `--carry-forward` | Non aggiorna mai i tassi BCE da solo esegui `rates --update` periodicamente; usa il sidecar di `classify` se presente |
119
+ | `classify <file.csv>` | `--offline` | Classifica gli strumenti (Bucket A/B) e crea/aggiorna il sidecar `*.classify.json` |
120
+ | `validate <file.csv>` | `--lang it\|en` | Exit 0 con avvisi; exit 1 in caso di errori bloccanti |
121
+ | `rates --check` | — | Mostra la copertura della snapshot BCE in locale |
122
+ | `rates --update` | — | Scarica i tassi aggiornati dall'API BCE |
123
+ | `config --lang it\|en` | — | Salva la lingua preferita |
124
+ | `config --show` | — | Mostra la lingua correntemente impostata |
125
+ | `stress-test` | `--range N-M`, `--keep`, `--json`, `--output-dir` | Documentato in fondo |
110
126
 
111
127
  Precedenza lingua: `--lang` > lingua salvata > italiano (default).
112
128
 
@@ -157,7 +173,7 @@ if (parser.warnings.length > 0) {
157
173
  // 2. Classificazione (Bucket A/B) — facoltativa, abilita Quadro RT/RM nel report
158
174
  const classification = await new Classifier().classify(
159
175
  transactions,
160
- "trades.classify.json", // sidecar persistente; omettilo per la modalità stateless
176
+ "trades.classify.json", // sidecar persistente; assente in modalità stateless
161
177
  );
162
178
 
163
179
  // 3. Calcolo
@@ -227,15 +243,15 @@ Configurazione tipica per un client MCP (es. Claude Desktop, `claude_desktop_con
227
243
 
228
244
  Il server espone 3 tool:
229
245
 
230
- | Tool | Descrizione |
231
- | ---------------------- | ------------------------------------------------------------------------------ |
232
- | `parse_transactions` | Effettua il parsing di un CSV DEGIRO in `transactions`/`warnings`/`incomeRows` |
233
- | `classify_instruments` | Classifica gli ISIN in Bucket A/B (modalità stateless — nessun sidecar) |
234
- | `calculate_gains` | Calcola plusvalenze/minusvalenze (LIFO/FIFO) e, se disponibile, Quadro RT/RM |
246
+ | Tool | Descrizione |
247
+ | ---------------------- | -------------------------------------------------------------------------------------- |
248
+ | `parse_transactions` | Esegue il parsing di un CSV DEGIRO, restituendo `transactions`/`warnings`/`incomeRows` |
249
+ | `classify_instruments` | Classifica gli ISIN in Bucket A/B (modalità stateless — nessun sidecar) |
250
+ | `calculate_gains` | Calcola plusvalenze/minusvalenze (LIFO/FIFO) e, se disponibile, Quadro RT/RM |
235
251
 
236
252
  `classify_instruments` supporta `existingClassification`, `overrides` e `offline: true` per
237
253
  funzionare senza rete e senza accesso al filesystem — pensato per essere invocato ripetutamente
238
- da un agente su più chiamate, mantenendo lo stato lato client.
254
+ da un agente in chiamate successive, mantenendo lo stato lato client.
239
255
 
240
256
  ### Domande frequenti
241
257
 
@@ -249,7 +265,7 @@ Le righe vengono saltate (senza bloccare il calcolo) quando: l'ISIN è vuoto, la
249
265
  Il CSV contiene una vendita per un titolo senza un acquisto precedente nel file. L'acquisto potrebbe essere in un export di un anno precedente non incluso. Usa `validate` per esaminare le transazioni parsate.
250
266
 
251
267
  **I tassi BCE sono scaduti**
252
- Esegui `minus-tracker rates --update`. Il comando `calc` esegue l'aggiornamento automaticamente se la snapshot ha più di 7 giorni.
268
+ Esegui `minus-tracker rates --update`. Il comando `calc` non aggiorna mai i tassi da solo: l'aggiornamento è sempre un'azione esplicita.
253
269
 
254
270
  **LIFO o FIFO?**
255
271
  Il Regime Dichiarativo utilizza il LIFO come metodo predefinito ai sensi della normativa fiscale italiana. Il FIFO è disponibile per confronto. Consulta il tuo commercialista per conferma.
@@ -393,16 +409,16 @@ npx @gabrielerandelli/minus-tracker calc trades.csv
393
409
 
394
410
  ### CLI Usage
395
411
 
396
- | Command | Key flags | Notes |
397
- | ---------------------- | -------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------- |
398
- | `calc <file.csv>` | `--method LIFO\|FIFO` (default: LIFO), `--lang it\|en`, `--json`, `--export-dichiarazione [path]`, `--carry-forward` | Auto-fetches ECB rates if snapshot is more than 7 days old; uses `classify`'s sidecar when present |
399
- | `classify <file.csv>` | `--offline` | Classifies instruments (Bucket A/B) and creates/updates the `*.classify.json` sidecar |
400
- | `validate <file.csv>` | `--lang it\|en` | Exit 0 with warnings; exit 1 on hard errors |
401
- | `rates --check` | — | Shows bundled ECB snapshot coverage |
402
- | `rates --update` | — | Fetches fresh rates from the ECB API |
403
- | `config --lang it\|en` | — | Saves language preference |
404
- | `config --show` | — | Shows current language setting |
405
- | `stress-test` | `--range N-M`, `--keep`, `--json`, `--output-dir` | Documented below |
412
+ | Command | Key flags | Notes |
413
+ | ---------------------- | -------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------- |
414
+ | `calc <file.csv>` | `--method LIFO\|FIFO` (default: LIFO), `--lang it\|en`, `--json`, `--export-dichiarazione [path]`, `--carry-forward` | Never fetches ECB rates on its own run `rates --update` periodically; uses `classify`'s sidecar when present |
415
+ | `classify <file.csv>` | `--offline` | Classifies instruments (Bucket A/B) and creates/updates the `*.classify.json` sidecar |
416
+ | `validate <file.csv>` | `--lang it\|en` | Exit 0 with warnings; exit 1 on hard errors |
417
+ | `rates --check` | — | Shows bundled ECB snapshot coverage |
418
+ | `rates --update` | — | Fetches fresh rates from the ECB API |
419
+ | `config --lang it\|en` | — | Saves language preference |
420
+ | `config --show` | — | Shows current language setting |
421
+ | `stress-test` | `--range N-M`, `--keep`, `--json`, `--output-dir` | Documented below |
406
422
 
407
423
  Language precedence: `--lang` flag > saved config > Italian (default).
408
424
 
@@ -543,7 +559,7 @@ Rows are skipped (without aborting the calculation) when: the ISIN is empty, the
543
559
  The CSV contains a SELL for a position that has no prior BUY in the same file. The BUY may be in a prior year's export that was not included. Use `validate` to inspect the parsed transactions.
544
560
 
545
561
  **ECB rates are outdated**
546
- Run `minus-tracker rates --update`. The `calc` command also auto-updates if the snapshot is more than 7 days old.
562
+ Run `minus-tracker rates --update`. The `calc` command never fetches rates on its own — refreshing is always an explicit, user-invoked action.
547
563
 
548
564
  **LIFO or FIFO?**
549
565
  LIFO is the standard lot-matching method under Italian tax law for the Regime Dichiarativo. FIFO is available for comparison or other jurisdictions. Consult your tax advisor for confirmation.
package/dist/cli/index.js CHANGED
@@ -9,6 +9,9 @@ var it = {
9
9
  errorInvalidCsv: "CSV non valido: impossibile analizzare il file",
10
10
  errorMissingColumn: (col) => `Colonna obbligatoria mancante: ${col}`,
11
11
  errorNoOpenLots: (isin, date) => `Nessun lotto aperto per ISIN ${isin} in data ${date}`,
12
+ errorCannotReadFile: (path7) => `Impossibile leggere il file: ${path7}`,
13
+ errorCannotLoadSidecar: (path7) => `Impossibile caricare il sidecar: ${path7}`,
14
+ errorCannotWriteExport: (path7) => `Impossibile scrivere l'export della dichiarazione: ${path7}`,
12
15
  warnMissingIsin: (row) => `Riga ${row}: ISIN mancante \u2014 riga ignorata`,
13
16
  warnUnsupportedCurrency: (row, currency) => `Riga ${row}: valuta non supportata ${currency} \u2014 riga ignorata`,
14
17
  warnNoEcbRate: (row, currency, date) => `Riga ${row}: nessun tasso BCE per ${currency} in data ${date} \u2014 riga ignorata`,
@@ -88,7 +91,7 @@ var it = {
88
91
  quadroRMRitenuta: "Ritenuta estera (credito)",
89
92
  quadroRMCedole: "[RM-C] Cedole obbligazionarie",
90
93
  dichiarazioneDisclaimer: "minus-tracker \xE8 un ausilio al calcolo, non consulenza fiscale.",
91
- warnNoDichiarazioneSidecar: "Eseguire prima: minus-tracker classify <file>",
94
+ warnNoDichiarazioneSidecar: "Suggerimento: esegui prima 'minus-tracker classify <file>' per abilitare il Quadro RT/RM.",
92
95
  warnNoCarryForwardProvided: "(eventuali perdite pregresse non applicate)"
93
96
  };
94
97
 
@@ -98,6 +101,9 @@ var en = {
98
101
  errorInvalidCsv: "Invalid CSV: unable to parse",
99
102
  errorMissingColumn: (col) => `Missing required column: ${col}`,
100
103
  errorNoOpenLots: (isin, date) => `No open lots for ISIN ${isin} on ${date}`,
104
+ errorCannotReadFile: (path7) => `Cannot read file: ${path7}`,
105
+ errorCannotLoadSidecar: (path7) => `Cannot load sidecar: ${path7}`,
106
+ errorCannotWriteExport: (path7) => `Cannot write dichiarazione export: ${path7}`,
101
107
  warnMissingIsin: (row) => `Row ${row}: missing ISIN \u2014 skipped`,
102
108
  warnUnsupportedCurrency: (row, currency) => `Row ${row}: unsupported currency ${currency} \u2014 skipped`,
103
109
  warnNoEcbRate: (row, currency, date) => `Row ${row}: no ECB rate for ${currency} on ${date} \u2014 skipped`,
@@ -177,7 +183,7 @@ var en = {
177
183
  quadroRMRitenuta: "Foreign withholding (credit)",
178
184
  quadroRMCedole: "[RM-C] Bond coupons",
179
185
  dichiarazioneDisclaimer: "minus-tracker \xE8 un ausilio al calcolo, non consulenza fiscale.",
180
- warnNoDichiarazioneSidecar: "Run first: minus-tracker classify <file>",
186
+ warnNoDichiarazioneSidecar: "Tip: run 'minus-tracker classify <file>' first to enable Quadro RT/RM.",
181
187
  warnNoCarryForwardProvided: "(prior-year losses not applied)"
182
188
  };
183
189
 
@@ -1689,8 +1695,7 @@ async function runCalc(positional, flags, s, stdout, stderr) {
1689
1695
  try {
1690
1696
  csv = fs4.readFileSync(filePath, "utf8");
1691
1697
  } catch {
1692
- stderr.write(`Cannot read file: ${filePath}
1693
- `);
1698
+ stderr.write(s.errorCannotReadFile(filePath) + "\n");
1694
1699
  return 1;
1695
1700
  }
1696
1701
  const method = flags["method"] ?? "LIFO";
@@ -1705,8 +1710,7 @@ async function runCalc(positional, flags, s, stdout, stderr) {
1705
1710
  const classifier = new Classifier({ interactive: false });
1706
1711
  classification = await classifier.load(sidecarPath);
1707
1712
  } catch {
1708
- stderr.write(`Cannot load sidecar: ${sidecarPath}
1709
- `);
1713
+ stderr.write(s.errorCannotLoadSidecar(sidecarPath) + "\n");
1710
1714
  return 1;
1711
1715
  }
1712
1716
  }
@@ -1776,8 +1780,7 @@ async function runCalc(positional, flags, s, stdout, stderr) {
1776
1780
  try {
1777
1781
  await report.dichiarazione.exportTo(exportPath);
1778
1782
  } catch {
1779
- stderr.write(`Cannot write dichiarazione export: ${exportPath}
1780
- `);
1783
+ stderr.write(s.errorCannotWriteExport(exportPath) + "\n");
1781
1784
  return 1;
1782
1785
  }
1783
1786
  }
@@ -1869,18 +1872,45 @@ function getSnapshotPath() {
1869
1872
  }
1870
1873
  return path4.join(configDir, "minus-tracker", "ecb-rates.json");
1871
1874
  }
1875
+ function countMissingBusinessDays(startDate, endDate, dates) {
1876
+ let missing = 0;
1877
+ const cursor = /* @__PURE__ */ new Date(startDate + "T00:00:00Z");
1878
+ const end = /* @__PURE__ */ new Date(endDate + "T00:00:00Z");
1879
+ while (cursor <= end) {
1880
+ const day = cursor.getUTCDay();
1881
+ const iso = cursor.toISOString().slice(0, 10);
1882
+ if (day !== 0 && day !== 6 && dates[iso] === void 0) missing++;
1883
+ cursor.setUTCDate(cursor.getUTCDate() + 1);
1884
+ }
1885
+ return missing;
1886
+ }
1872
1887
  function getCoverage(snapshot) {
1873
1888
  let start = "9999-12-31";
1874
1889
  let end = "0000-01-01";
1875
1890
  const currencies = [];
1891
+ const gapEntries = [];
1876
1892
  for (const [ccy, dates] of Object.entries(snapshot)) {
1877
1893
  currencies.push(ccy);
1878
- for (const d of Object.keys(dates)) {
1894
+ const keys = Object.keys(dates).sort();
1895
+ for (const d of keys) {
1879
1896
  if (d < start) start = d;
1880
1897
  if (d > end) end = d;
1881
1898
  }
1899
+ if (keys.length > 0) {
1900
+ const missing = countMissingBusinessDays(
1901
+ keys[0],
1902
+ keys[keys.length - 1],
1903
+ dates
1904
+ );
1905
+ if (missing > 0) gapEntries.push(`${ccy}: ${missing}`);
1906
+ }
1882
1907
  }
1883
- return { start, end, currencies: currencies.sort().join(", ") };
1908
+ return {
1909
+ start,
1910
+ end,
1911
+ currencies: currencies.sort().join(", "),
1912
+ gaps: gapEntries.sort().join(", ")
1913
+ };
1884
1914
  }
1885
1915
  async function fetchEcbData(currency) {
1886
1916
  return new Promise((resolve2, reject) => {
@@ -1944,9 +1974,9 @@ async function updateRates(snapshotPath, stdout, stderr, s) {
1944
1974
  async function runRates(positional, flags, s, stdout, stderr) {
1945
1975
  if (flags["check"]) {
1946
1976
  const snapshot = getActiveSnapshot();
1947
- const { start, end, currencies } = getCoverage(snapshot);
1977
+ const { start, end, currencies, gaps } = getCoverage(snapshot);
1948
1978
  stdout.write(s.ratesCoverage(start, end, currencies) + "\n");
1949
- stdout.write(s.ratesGapsNone + "\n");
1979
+ stdout.write((gaps ? s.ratesGaps(gaps) : s.ratesGapsNone) + "\n");
1950
1980
  return 0;
1951
1981
  }
1952
1982
  if (flags["update"]) {
@@ -2512,7 +2542,9 @@ async function main() {
2512
2542
  range: { type: "string" },
2513
2543
  keep: { type: "boolean", default: false },
2514
2544
  "output-dir": { type: "string" },
2515
- offline: { type: "boolean", default: false }
2545
+ offline: { type: "boolean", default: false },
2546
+ "carry-forward": { type: "string", multiple: true },
2547
+ "export-dichiarazione": { type: "string" }
2516
2548
  },
2517
2549
  allowPositionals: true,
2518
2550
  strict: false
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/cli/index.ts","../../src/i18n/it.ts","../../src/i18n/en.ts","../../src/i18n/settings.ts","../../src/i18n/index.ts","../../src/errors.ts","../../src/cli/commands/calc.ts","../../src/rates/index.ts","../../src/parser/warnings.ts","../../src/parser/index.ts","../../src/dichiarazione/engine.ts","../../src/calculator/index.ts","../../src/classifier/index.ts","../../src/cli/renderer.ts","../../src/cli/commands/validate.ts","../../src/cli/commands/rates.ts","../../src/cli/commands/config.ts","../../src/cli/commands/stress-test.ts","../../src/stress/generator.ts","../../src/stress/runner.ts","../../src/stress/reporter.ts","../../src/cli/commands/classify.ts"],"sourcesContent":["import { parseArgs } from \"node:util\";\nimport { resolveLocale, getStrings } from \"../i18n/index.js\";\nimport { ParseError, CalculationError } from \"../errors.js\";\nimport { runCalc } from \"./commands/calc.js\";\nimport { runValidate } from \"./commands/validate.js\";\nimport { runRates } from \"./commands/rates.js\";\nimport { runConfig } from \"./commands/config.js\";\nimport { runStressTest } from \"./commands/stress-test.js\";\nimport { runClassify } from \"./commands/classify.js\";\nimport { ClassificationError } from \"../errors.js\";\n\nasync function main(): Promise<void> {\n const { values, positionals } = parseArgs({\n args: process.argv.slice(2),\n options: {\n lang: { type: \"string\" },\n method: { type: \"string\" },\n year: { type: \"string\" },\n json: { type: \"boolean\", default: false },\n check: { type: \"boolean\", default: false },\n update: { type: \"boolean\", default: false },\n show: { type: \"boolean\", default: false },\n range: { type: \"string\" },\n keep: { type: \"boolean\", default: false },\n \"output-dir\": { type: \"string\" },\n offline: { type: \"boolean\", default: false },\n },\n allowPositionals: true,\n strict: false,\n });\n\n // Resolve locale once — exits 2 on invalid value\n const locale = resolveLocale(values.lang as string | undefined);\n const s = getStrings(locale);\n\n const command = positionals[0];\n const restPositionals = positionals.slice(1);\n const flags = values as Record<string, string | boolean>;\n\n const stdout = process.stdout;\n const stderr = process.stderr;\n\n let exitCode = 0;\n\n try {\n switch (command) {\n case \"calc\":\n exitCode = await runCalc(restPositionals, flags, s, stdout, stderr);\n break;\n case \"validate\":\n exitCode = await runValidate(restPositionals, flags, s, stdout, stderr);\n break;\n case \"rates\":\n exitCode = await runRates(restPositionals, flags, s, stdout, stderr);\n break;\n case \"config\":\n exitCode = await runConfig(restPositionals, flags, s, stdout, stderr);\n break;\n case \"stress-test\":\n exitCode = await runStressTest(restPositionals, flags, stdout, stderr);\n break;\n case \"classify\":\n exitCode = await runClassify(restPositionals, flags, s, stdout, stderr);\n break;\n default:\n stderr.write(\n \"Usage: minus-tracker <calc|validate|rates|config|stress-test|classify> [options] [file]\\n\",\n );\n exitCode = 2;\n }\n } catch (err) {\n if (err instanceof ClassificationError) {\n stderr.write(err.message + \"\\n\");\n exitCode = 1;\n } else if (err instanceof ParseError) {\n if (err.code === \"INVALID_CSV\") {\n stderr.write(s.errorInvalidCsv + \"\\n\");\n } else {\n stderr.write(s.errorMissingColumn(err.columnName!) + \"\\n\");\n }\n exitCode = 1;\n } else if (err instanceof CalculationError) {\n stderr.write(s.errorNoOpenLots(err.isin, err.date) + \"\\n\");\n exitCode = 1;\n } else {\n throw err;\n }\n }\n\n process.exit(exitCode);\n}\n\nmain().catch((err) => {\n process.stderr.write(`Unhandled error: ${String(err)}\\n`);\n process.exit(1);\n});\n","import type { LocaleStrings } from \"./types.js\";\n\nexport const it: LocaleStrings = {\n numberLocale: \"it-IT\",\n\n errorInvalidCsv: \"CSV non valido: impossibile analizzare il file\",\n errorMissingColumn: (col) => `Colonna obbligatoria mancante: ${col}`,\n errorNoOpenLots: (isin, date) =>\n `Nessun lotto aperto per ISIN ${isin} in data ${date}`,\n\n warnMissingIsin: (row) => `Riga ${row}: ISIN mancante — riga ignorata`,\n warnUnsupportedCurrency: (row, currency) =>\n `Riga ${row}: valuta non supportata ${currency} — riga ignorata`,\n warnNoEcbRate: (row, currency, date) =>\n `Riga ${row}: nessun tasso BCE per ${currency} in data ${date} — riga ignorata`,\n warnQuantityZero: (row) => `Riga ${row}: quantità pari a 0 — riga ignorata`,\n warnMissingIsinIncome: (row) =>\n `Riga ${row}: ISIN mancante su riga di reddito — riga ignorata`,\n warnOrphanWithholding: (isin, date) =>\n `Riga di ritenuta su ${isin}/${date} senza riga di reddito corrispondente — riga ignorata`,\n\n warnMultipleYears:\n \"Il CSV contiene transazioni di più anni — filtra per un singolo anno per un calcolo accurato.\",\n\n headerMethod: \"METODO\",\n headerTaxYear: \"ANNO FISCALE\",\n headerIsin: \"ISIN\",\n headerProduct: \"TITOLO\",\n headerQty: \"QTÀ\",\n headerBuyDate: \"DATA ACQUISTO\",\n headerSellDate: \"DATA VENDITA\",\n headerBuyEur: \"ACQUISTO EUR\",\n headerSellEur: \"VENDITA EUR\",\n headerGainLoss: \"GUADAGNO/PERDITA\",\n\n summaryPlusvalenze: \"PLUSVALENZE\",\n summaryMinusvalenze: \"MINUSVALENZE\",\n summaryNetResult: \"RISULTATO NETTO\",\n summaryWarnings: \"AVVERTENZE\",\n summaryGenerated: \"Generato\",\n\n validateOk: (count, errors) =>\n `OK: ${count} transazioni analizzate, ${errors} errori gravi`,\n validateWarn: (count, reason) =>\n `AVVISO: ${count} righe ignorate (${reason})`,\n\n ratesCoverage: (start, end, currencies) =>\n `Copertura: ${start} → ${end} | Valute: ${currencies}`,\n ratesGapsNone: \"Lacune: nessuna\",\n ratesGaps: (list) => `Lacune: ${list}`,\n ratesUpdateFetching: \"Recupero dati BCE SDMX in corso...\",\n ratesUpdateDone: (n) => `Completato. Aggiunte ${n} nuove date.`,\n ratesSnapshotWritten: (path) => `Snapshot scritto in ${path}`,\n ratesAutoUpdateStart:\n \"Le rate BCE non sono aggiornate. Aggiornamento automatico in corso...\",\n ratesAutoUpdateFailed:\n \"Aggiornamento automatico fallito. Si procede con le rate disponibili.\",\n\n configLangSet: (lang) => `Lingua impostata su: ${lang}`,\n configCurrentLang: (lang) => `Lingua corrente: ${lang}`,\n\n classifyStarting: (n) => `Classificazione ISIN in corso... (${n} strumenti)`,\n classifyDetected: (isin, product, bucket, rate) =>\n `Rilevato: ${product} (${isin}) → ${bucket} | ${rate}`,\n classifyConfirm: \"[Y/n/?]: \",\n classifyOverridePrompt: \"Tipo di strumento:\",\n classifyDone: (confirmed, total) =>\n `Classificazione completata (${confirmed}/${total} confermati).`,\n classifyWritten: (path) => `Scritto: ${path}`,\n classifyMergePrompt: (existing) =>\n `File già esistente (${existing} ISIN confermati). Aggiungere i nuovi ISIN? [Y/n]: `,\n classifyOfflineWarning: \"Modalità offline: ricerca OpenFIGI saltata.\",\n classifyUnknownType: (type) =>\n `Tipo non riconosciuto: ${type}. Classificare manualmente.`,\n classifyNonTtyError:\n \"Modalità interattiva richiede un terminale (TTY). Usa --offline per classificare senza terminale.\",\n\n bucketAHeader: \"BUCKET A — REDDITI DA CAPITALE (non compensabili)\",\n bucketBHeader: \"BUCKET B — REDDITI DIVERSI\",\n bucketAEtf: \"ETF (OICR)\",\n bucketABtpWl: \"BTP/WL\",\n bucketATotalTax: \"TOTALE IMPOSTA\",\n bucketBCarryApplied: (year) => `RIPORTO ${year}`,\n bucketBResult: \"RISULTATO\",\n bucketBCarryNote: \"(riportabile ai prossimi 4 anni)\",\n warnMixedBuckets:\n \"Le perdite in Bucket B non compensano le plusvalenze in Bucket A.\",\n warnMixedAssets:\n \"AVVISO: CSV contiene tipi di strumenti misti (es. ETF + Azioni).\\n\" +\n \" Il calcolo a bucket unico può non essere fiscalmente corretto.\\n\" +\n \" Esegui: minus-tracker classify trades.csv\",\n headerBucket: \"BUCKET\",\n warnUnclassifiedIsin: (isin) =>\n `ISIN ${isin} non trovato nella mappa di classificazione — assegnato a Bucket B.`,\n carryForwardInvalidFormat:\n \"Formato --carry-forward non valido. Usa: AAAA:importo (es. 2023:2500)\",\n\n disclaimer: \"minus-tracker è un ausilio al calcolo, non consulenza fiscale.\",\n\n // v0.7.0 dichiarazione section\n dichiarazioneHeader: (anno) =>\n `── MODELLO REDDITI PF [ANNO D'IMPOSTA: ${anno}]`,\n dichiarazioneNota:\n \"Nota: i valori riportano i codici riga indicativi — verificare la modulistica ufficiale.\",\n dichiarazioneWarningRow:\n \"⚠ I codici riga tra parentesi quadre sono indicativi. Verificare la versione corrente del modello.\",\n quadroRTHeader: \"QUADRO RT — SEZIONE II (redditi diversi)\",\n quadroRTPlusvalenze: \"[RT-P] Plusvalenze totali\",\n quadroRTMinusvalenze: \"[RT-M] Minusvalenze totali\",\n quadroRTDifferenza: \"[RT-D] Differenza\",\n quadroRTRiporto: (anno) => `[RT-C*] Riporto ${anno} (consumato)`,\n quadroRTImponibile: \"[RT-N] Imponibile netto\",\n quadroRTImposta: \"[RT-I] Imposta (26%)\",\n quadroRTRiportabile: \"[RT-R] Minusvalenze da riportare\",\n quadroRTPerdita: \"perdita netta:\",\n quadroRMHeader: \"QUADRO RM (redditi da capitale)\",\n quadroRMEtf26: \"[RM-A1] ETF/OICR plusvalenze (26%)\",\n quadroRMImposta: \"Imposta sostitutiva\",\n quadroRMBtp: \"[RM-A2] BTP/WL plusvalenze (12,5%)\",\n quadroRMDividendi: \"[RM-D] Dividendi esteri lordi\",\n quadroRMRitenuta: \"Ritenuta estera (credito)\",\n quadroRMCedole: \"[RM-C] Cedole obbligazionarie\",\n dichiarazioneDisclaimer:\n \"minus-tracker è un ausilio al calcolo, non consulenza fiscale.\",\n warnNoDichiarazioneSidecar: \"Eseguire prima: minus-tracker classify <file>\",\n warnNoCarryForwardProvided: \"(eventuali perdite pregresse non applicate)\",\n};\n","import type { LocaleStrings } from \"./types.js\";\n\nexport const en: LocaleStrings = {\n numberLocale: \"en-US\",\n\n errorInvalidCsv: \"Invalid CSV: unable to parse\",\n errorMissingColumn: (col) => `Missing required column: ${col}`,\n errorNoOpenLots: (isin, date) => `No open lots for ISIN ${isin} on ${date}`,\n\n warnMissingIsin: (row) => `Row ${row}: missing ISIN — skipped`,\n warnUnsupportedCurrency: (row, currency) =>\n `Row ${row}: unsupported currency ${currency} — skipped`,\n warnNoEcbRate: (row, currency, date) =>\n `Row ${row}: no ECB rate for ${currency} on ${date} — skipped`,\n warnQuantityZero: (row) => `Row ${row}: quantity is 0 — skipped`,\n warnMissingIsinIncome: (row) =>\n `Row ${row}: income row has blank ISIN — skipped`,\n warnOrphanWithholding: (isin, date) =>\n `Withholding row on ${isin}/${date} has no matching income row — skipped`,\n\n warnMultipleYears:\n \"CSV contains transactions from multiple years — filter to a single year for accurate reporting.\",\n\n headerMethod: \"METHOD\",\n headerTaxYear: \"TAX YEAR\",\n headerIsin: \"ISIN\",\n headerProduct: \"PRODUCT\",\n headerQty: \"QTY\",\n headerBuyDate: \"BUY DATE\",\n headerSellDate: \"SELL DATE\",\n headerBuyEur: \"BUY EUR\",\n headerSellEur: \"SELL EUR\",\n headerGainLoss: \"GAIN/LOSS\",\n\n summaryPlusvalenze: \"PLUSVALENZE\",\n summaryMinusvalenze: \"MINUSVALENZE\",\n summaryNetResult: \"NET RESULT\",\n summaryWarnings: \"WARNINGS\",\n summaryGenerated: \"Generated\",\n\n validateOk: (count, errors) =>\n `OK: ${count} transactions parsed, ${errors} hard errors`,\n validateWarn: (count, reason) => `WARN: ${count} rows skipped (${reason})`,\n\n ratesCoverage: (start, end, currencies) =>\n `Coverage: ${start} → ${end} | Currencies: ${currencies}`,\n ratesGapsNone: \"Gaps: none\",\n ratesGaps: (list) => `Gaps: ${list}`,\n ratesUpdateFetching: \"Fetching ECB SDMX...\",\n ratesUpdateDone: (n) => `Done. Added ${n} new dates.`,\n ratesSnapshotWritten: (path) => `Snapshot written to ${path}`,\n ratesAutoUpdateStart: \"ECB rates are stale. Auto-updating…\",\n ratesAutoUpdateFailed: \"Auto-update failed. Proceeding with available rates.\",\n\n configLangSet: (lang) => `Language set to: ${lang}`,\n configCurrentLang: (lang) => `Current language: ${lang}`,\n\n classifyStarting: (n) => `Classifying ISINs... (${n} instruments)`,\n classifyDetected: (isin, product, bucket, rate) =>\n `Detected: ${product} (${isin}) → ${bucket} | ${rate}`,\n classifyConfirm: \"[Y/n/?]: \",\n classifyOverridePrompt: \"Instrument type:\",\n classifyDone: (confirmed, total) =>\n `Classification complete (${confirmed}/${total} confirmed).`,\n classifyWritten: (path) => `Written: ${path}`,\n classifyMergePrompt: (existing) =>\n `File already exists (${existing} confirmed ISINs). Add new ISINs? [Y/n]: `,\n classifyOfflineWarning: \"Offline mode: OpenFIGI lookup skipped.\",\n classifyUnknownType: (type) =>\n `Unrecognized type: ${type}. Please classify manually.`,\n classifyNonTtyError:\n \"Interactive mode requires a TTY. Use --offline to classify without a terminal.\",\n\n bucketAHeader: \"BUCKET A — CAPITAL INCOME (non-offsettable)\",\n bucketBHeader: \"BUCKET B — MISCELLANEOUS INCOME\",\n bucketAEtf: \"ETF (OICR)\",\n bucketABtpWl: \"Govt Bond WL\",\n bucketATotalTax: \"TOTAL TAX\",\n bucketBCarryApplied: (year) => `CARRY ${year}`,\n bucketBResult: \"RESULT\",\n bucketBCarryNote: \"(carriable over the next 4 years)\",\n warnMixedBuckets: \"Bucket B losses do not offset Bucket A plusvalenze.\",\n warnMixedAssets:\n \"WARNING: CSV contains mixed instrument types (e.g. ETFs + Stocks).\\n\" +\n \" Single-bucket calculation may not be fiscally correct.\\n\" +\n \" Run: minus-tracker classify trades.csv\",\n headerBucket: \"BUCKET\",\n warnUnclassifiedIsin: (isin) =>\n `ISIN ${isin} not found in classification map — assigned to Bucket B.`,\n carryForwardInvalidFormat:\n \"Invalid --carry-forward format. Use: YYYY:amount (e.g. 2023:2500)\",\n\n disclaimer: \"minus-tracker è un ausilio al calcolo, non consulenza fiscale.\",\n\n // v0.7.0 dichiarazione section\n dichiarazioneHeader: (anno) => `── MODELLO REDDITI PF [TAX YEAR: ${anno}]`,\n dichiarazioneNota:\n \"Note: row codes are indicative — verify against the official tax form.\",\n dichiarazioneWarningRow:\n \"⚠ Row codes in square brackets are indicative. Check the current form version.\",\n quadroRTHeader: \"QUADRO RT — SECTION II (capital gains)\",\n quadroRTPlusvalenze: \"[RT-P] Total gains\",\n quadroRTMinusvalenze: \"[RT-M] Total losses\",\n quadroRTDifferenza: \"[RT-D] Difference\",\n quadroRTRiporto: (anno) => `[RT-C*] Prior loss ${anno} (applied)`,\n quadroRTImponibile: \"[RT-N] Net taxable\",\n quadroRTImposta: \"[RT-I] Tax (26%)\",\n quadroRTRiportabile: \"[RT-R] Losses to carry forward\",\n quadroRTPerdita: \"net loss:\",\n quadroRMHeader: \"QUADRO RM (capital income)\",\n quadroRMEtf26: \"[RM-A1] ETF/OICR gains (26%)\",\n quadroRMImposta: \"Substitute tax\",\n quadroRMBtp: \"[RM-A2] BTP/WL gains (12.5%)\",\n quadroRMDividendi: \"[RM-D] Foreign dividends (gross)\",\n quadroRMRitenuta: \"Foreign withholding (credit)\",\n quadroRMCedole: \"[RM-C] Bond coupons\",\n dichiarazioneDisclaimer:\n \"minus-tracker è un ausilio al calcolo, non consulenza fiscale.\",\n warnNoDichiarazioneSidecar: \"Run first: minus-tracker classify <file>\",\n warnNoCarryForwardProvided: \"(prior-year losses not applied)\",\n};\n","import * as fs from \"node:fs\";\nimport * as os from \"node:os\";\nimport * as path from \"node:path\";\nimport type { MinusTrackerConfig, SupportedLocale } from \"./types.js\";\n\nconst SUPPORTED_LOCALES: SupportedLocale[] = [\"it\", \"en\"];\n\nexport function getConfigPath(): string {\n if (process.platform === \"win32\") {\n const appData =\n process.env.APPDATA ?? path.join(os.homedir(), \"AppData\", \"Roaming\");\n return path.join(appData, \"minus-tracker\", \"config.json\");\n }\n const xdg = process.env.XDG_CONFIG_HOME ?? path.join(os.homedir(), \".config\");\n return path.join(xdg, \"minus-tracker\", \"config.json\");\n}\n\nexport function readConfig(): MinusTrackerConfig {\n try {\n const raw = fs.readFileSync(getConfigPath(), \"utf8\");\n return JSON.parse(raw) as MinusTrackerConfig;\n } catch {\n return {};\n }\n}\n\nexport function saveLocale(lang: SupportedLocale): void {\n const configPath = getConfigPath();\n fs.mkdirSync(path.dirname(configPath), { recursive: true });\n const existing = readConfig();\n const updated: MinusTrackerConfig = { ...existing, locale: lang };\n fs.writeFileSync(configPath, JSON.stringify(updated, null, 2) + \"\\n\", \"utf8\");\n}\n\nexport function resolveLocale(cliLang?: string): SupportedLocale {\n // 1. --lang flag\n if (cliLang !== undefined) {\n if (!SUPPORTED_LOCALES.includes(cliLang as SupportedLocale)) {\n process.stderr.write(`Unsupported locale \"${cliLang}\". Use: it, en\\n`);\n process.exit(2);\n }\n return cliLang as SupportedLocale;\n }\n // 2. env var\n const envLang = process.env.MINUS_TRACKER_LANG;\n if (envLang !== undefined) {\n if (!SUPPORTED_LOCALES.includes(envLang as SupportedLocale)) {\n process.stderr.write(`Unsupported locale \"${envLang}\". Use: it, en\\n`);\n process.exit(2);\n }\n return envLang as SupportedLocale;\n }\n // 3. config file\n const config = readConfig();\n if (config.locale && SUPPORTED_LOCALES.includes(config.locale)) {\n return config.locale;\n }\n // 4. default\n return \"it\";\n}\n","import { it } from \"./it.js\";\nimport { en } from \"./en.js\";\nimport type { LocaleStrings, SupportedLocale } from \"./types.js\";\n\nexport { it, en };\nexport type { LocaleStrings, SupportedLocale };\nexport type { MinusTrackerConfig } from \"./types.js\";\nexport {\n resolveLocale,\n saveLocale,\n readConfig,\n getConfigPath,\n} from \"./settings.js\";\n\nexport function getStrings(locale: SupportedLocale): LocaleStrings {\n return locale === \"it\" ? it : en;\n}\n","export class ParseError extends Error {\n readonly code: \"INVALID_CSV\" | \"MISSING_COLUMN\";\n readonly columnName?: string;\n\n constructor(code: \"INVALID_CSV\");\n constructor(code: \"MISSING_COLUMN\", columnName: string);\n constructor(code: ParseError[\"code\"], columnName?: string) {\n const msg =\n code === \"INVALID_CSV\"\n ? \"Invalid CSV: unable to parse\"\n : `Missing required column: ${columnName}`;\n super(msg);\n this.name = \"ParseError\";\n this.code = code;\n this.columnName = columnName;\n }\n}\n\nexport class CalculationError extends Error {\n readonly isin: string;\n readonly date: string;\n\n constructor(isin: string, date: string) {\n super(`No open lots for ISIN ${isin} on ${date}`);\n this.name = \"CalculationError\";\n this.isin = isin;\n this.date = date;\n }\n}\n\nexport class ClassificationError extends Error {\n readonly code:\n | \"NETWORK_ERROR\"\n | \"SIDECAR_NOT_FOUND\"\n | \"SIDECAR_VERSION\"\n | \"SIDECAR_MALFORMED\"\n | \"WRITE_ERROR\";\n\n constructor(code: ClassificationError[\"code\"], message?: string) {\n super(\n message ??\n (code === \"SIDECAR_NOT_FOUND\"\n ? \"Sidecar file not found\"\n : code === \"SIDECAR_VERSION\"\n ? \"Sidecar version mismatch\"\n : code === \"SIDECAR_MALFORMED\"\n ? \"Sidecar file is malformed JSON\"\n : code === \"NETWORK_ERROR\"\n ? \"Network error contacting OpenFIGI\"\n : \"Failed to write sidecar file\"),\n );\n this.name = \"ClassificationError\";\n this.code = code;\n }\n}\n","import * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport * as os from \"node:os\";\nimport { DEGIROParser } from \"../../parser/index.js\";\nimport { Calculator } from \"../../calculator/index.js\";\nimport { Classifier } from \"../../classifier/index.js\";\nimport { ParseError } from \"../../errors.js\";\nimport { renderReport } from \"../renderer.js\";\nimport type { LocaleStrings } from \"../../i18n/types.js\";\nimport type {\n LotMethod,\n ClassificationMap,\n CarryForward,\n} from \"../../types.js\";\n\nexport async function runCalc(\n positional: string[],\n flags: Record<string, string | boolean>,\n s: LocaleStrings,\n stdout: NodeJS.WritableStream,\n stderr: NodeJS.WritableStream,\n): Promise<number> {\n const filePath = positional[0];\n if (!filePath) {\n stderr.write(\n \"Usage: minus-tracker calc [--method LIFO|FIFO] [--json] <file.csv>\\n\",\n );\n return 2;\n }\n\n let csv: string;\n try {\n csv = fs.readFileSync(filePath, \"utf8\");\n } catch {\n stderr.write(`Cannot read file: ${filePath}\\n`);\n return 1;\n }\n\n const method = (flags[\"method\"] as LotMethod) ?? \"LIFO\";\n if (method !== \"LIFO\" && method !== \"FIFO\") {\n stderr.write(\"--method must be LIFO or FIFO\\n\");\n return 2;\n }\n\n // Sidecar auto-discovery\n const sidecarPath = filePath.replace(/\\.csv$/i, \"\") + \".classify.json\";\n let classification: ClassificationMap | undefined;\n if (fs.existsSync(sidecarPath)) {\n try {\n const classifier = new Classifier({ interactive: false });\n classification = await classifier.load(sidecarPath);\n } catch {\n stderr.write(`Cannot load sidecar: ${sidecarPath}\\n`);\n return 1;\n }\n }\n\n // --export-dichiarazione flag parsing (optional path value)\n const exportFlagRaw = flags[\"export-dichiarazione\"];\n const exportRequested =\n exportFlagRaw !== undefined && exportFlagRaw !== false;\n const exportPath =\n typeof exportFlagRaw === \"string\"\n ? exportFlagRaw\n : filePath.replace(/\\.csv$/i, \"\") + \".dichiarazione.json\";\n\n if (exportRequested && classification === undefined) {\n stderr.write(s.warnNoDichiarazioneSidecar + \"\\n\");\n return 1;\n }\n\n // --carry-forward flag parsing\n const rawCf: unknown = flags[\"carry-forward\"];\n const rawCarryForwards: string[] = Array.isArray(rawCf)\n ? (rawCf as string[])\n : rawCf\n ? [rawCf as string]\n : [];\n\n const cfRegex = /^\\d{4}:\\d+(\\.\\d+)?$/;\n for (const cf of rawCarryForwards) {\n if (!cfRegex.test(cf)) {\n stderr.write(s.carryForwardInvalidFormat + \"\\n\");\n return 2;\n }\n }\n\n // Load carry-forward config file\n const xdgConfig =\n process.env[\"XDG_CONFIG_HOME\"] ?? path.join(os.homedir(), \".config\");\n const cfConfigPath = path.join(\n xdgConfig,\n \"minus-tracker\",\n \"carryforward.json\",\n );\n const cfFromFile: Record<number, number> = {};\n if (fs.existsSync(cfConfigPath)) {\n try {\n const raw = JSON.parse(fs.readFileSync(cfConfigPath, \"utf-8\")) as {\n losses?: Array<{ year: number; amount: number }>;\n };\n for (const entry of raw.losses ?? []) {\n cfFromFile[entry.year] = entry.amount;\n }\n } catch {\n /* silently skip malformed config */\n }\n }\n\n // Merge: flag wins over config file\n const cfMerged: Record<number, number> = { ...cfFromFile };\n for (const cf of rawCarryForwards) {\n const [yearStr, amountStr] = cf.split(\":\");\n cfMerged[parseInt(yearStr, 10)] = parseFloat(amountStr);\n }\n const carryForward: CarryForward[] = Object.entries(cfMerged).map(\n ([year, amount]) => ({ year: parseInt(year, 10), amount }),\n );\n\n const parser = new DEGIROParser();\n let transactions;\n try {\n transactions = parser.parse(csv);\n } catch (err) {\n if (err instanceof ParseError) {\n if (err.code === \"INVALID_CSV\") {\n stderr.write(s.errorInvalidCsv + \"\\n\");\n } else {\n stderr.write(s.errorMissingColumn(err.columnName!) + \"\\n\");\n }\n return 1;\n }\n throw err;\n }\n\n const calculator = new Calculator(transactions, parser.warnings, {\n classification,\n carryForward: carryForward.length > 0 ? carryForward : undefined,\n incomeRows: parser.incomeRows,\n });\n const report = calculator.calculateGains(method);\n const carryForwardWasProvided = carryForward.length > 0;\n\n if (exportRequested) {\n try {\n await report.dichiarazione!.exportTo(exportPath);\n } catch {\n stderr.write(`Cannot write dichiarazione export: ${exportPath}\\n`);\n return 1;\n }\n }\n\n if (flags[\"json\"]) {\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n const { dichiarazione: _dichiarazione, ...jsonReport } = report;\n stdout.write(JSON.stringify(jsonReport, null, 2) + \"\\n\");\n } else {\n stdout.write(renderReport(report, s, carryForwardWasProvided) + \"\\n\");\n }\n\n if (exportRequested) {\n stdout.write(s.classifyWritten(exportPath) + \"\\n\");\n } else if (!report.dichiarazione) {\n stderr.write(s.warnNoDichiarazioneSidecar + \"\\n\");\n }\n\n return 0;\n}\n","import { fileURLToPath } from \"node:url\";\nimport * as path from \"node:path\";\nimport * as fs from \"node:fs\";\n\nexport type RatesSnapshot = Record<string, Record<string, number>>;\n\n// Load the bundled snapshot lazily; returns null if the file is absent (e.g. broken install)\nlet _bundled: RatesSnapshot | null | undefined = undefined;\n\nfunction getBundledSnapshot(): RatesSnapshot | null {\n if (_bundled !== undefined) return _bundled;\n const __dirname = path.dirname(fileURLToPath(import.meta.url));\n const bundledPath = path.join(__dirname, \"../data/ecb-rates.json\");\n try {\n _bundled = JSON.parse(\n fs.readFileSync(bundledPath, \"utf8\"),\n ) as RatesSnapshot;\n } catch {\n _bundled = null;\n }\n return _bundled;\n}\n\nfunction getUserSnapshot(): RatesSnapshot | null {\n const platform = process.platform;\n let configDir: string;\n if (platform === \"win32\") {\n configDir =\n process.env.APPDATA ??\n path.join(process.env.USERPROFILE ?? \"~\", \"AppData\", \"Roaming\");\n } else {\n configDir =\n process.env.XDG_CONFIG_HOME ??\n path.join(process.env.HOME ?? \"~\", \".config\");\n }\n const userPath = path.join(configDir, \"minus-tracker\", \"ecb-rates.json\");\n try {\n return JSON.parse(fs.readFileSync(userPath, \"utf8\")) as RatesSnapshot;\n } catch {\n return null;\n }\n}\n\nexport function getActiveSnapshot(): RatesSnapshot {\n const bundled = getBundledSnapshot();\n const user = getUserSnapshot();\n if (!bundled && !user) {\n throw new Error(\n \"No ECB rates snapshot available. Run `minus-tracker rates --update` to fetch rates.\",\n );\n }\n if (!bundled) return user!;\n if (!user) return bundled;\n // Merge: user entries override bundled for same date keys\n const merged: RatesSnapshot = {};\n for (const ccy of new Set([...Object.keys(bundled), ...Object.keys(user)])) {\n merged[ccy] = { ...(bundled[ccy] ?? {}), ...(user[ccy] ?? {}) };\n }\n return merged;\n}\n\nexport function isSnapshotStale(\n snapshot: RatesSnapshot,\n today?: string,\n): boolean {\n const todayStr = today ?? new Date().toISOString().slice(0, 10);\n let maxDate = \"0000-01-01\";\n for (const dates of Object.values(snapshot)) {\n for (const d of Object.keys(dates)) {\n if (d > maxDate) maxDate = d;\n }\n }\n if (maxDate === \"0000-01-01\") return true;\n const diffDays =\n (new Date(todayStr).getTime() - new Date(maxDate).getTime()) /\n (1000 * 60 * 60 * 24);\n return diffDays >= 7;\n}\n\nfunction subtractDays(isoDate: string, days: number): string {\n const d = new Date(isoDate + \"T00:00:00Z\");\n d.setUTCDate(d.getUTCDate() - days);\n return d.toISOString().slice(0, 10);\n}\n\n/**\n * Look up ECB rate for a currency on a given date.\n * Returns 1.0 for EUR. Walks back up to 3 calendar days for weekend/holiday gaps.\n * Returns null if not found within the window.\n *\n * @param currency ISO 4217 currency code\n * @param date YYYY-MM-DD trade date\n * @param snapshot Optional override (used in tests to inject stub data)\n */\nexport function lookupRate(\n currency: string,\n date: string,\n snapshot?: RatesSnapshot,\n): number | null {\n if (currency === \"EUR\") return 1.0;\n\n const s = snapshot ?? getActiveSnapshot();\n const currencyRates = s[currency];\n if (!currencyRates) return null; // unsupported currency\n\n for (let i = 0; i <= 3; i++) {\n const d = i === 0 ? date : subtractDays(date, i);\n if (currencyRates[d] !== undefined) {\n return currencyRates[d];\n }\n }\n return null;\n}\n","export type WarningEntry =\n | { code: \"MISSING_ISIN\"; row: number }\n | { code: \"UNSUPPORTED_CURRENCY\"; row: number; currency: string }\n | { code: \"NO_ECB_RATE\"; row: number; currency: string; date: string }\n | { code: \"QUANTITY_ZERO\"; row: number }\n | { code: \"MISSING_ISIN_INCOME\"; row: number }\n | { code: \"ORPHAN_WITHHOLDING\"; isin: string; date: string };\n\nexport function warningToEnglish(w: WarningEntry): string {\n switch (w.code) {\n case \"MISSING_ISIN\":\n return `Row ${w.row}: missing ISIN — skipped`;\n case \"UNSUPPORTED_CURRENCY\":\n return `Row ${w.row}: unsupported currency ${w.currency} — skipped`;\n case \"NO_ECB_RATE\":\n return `Row ${w.row}: no ECB rate for ${w.currency} on ${w.date} — skipped`;\n case \"QUANTITY_ZERO\":\n return `Row ${w.row}: quantity is 0 — skipped`;\n case \"MISSING_ISIN_INCOME\":\n return `Row ${w.row}: blank ISIN on income row — skipped`;\n case \"ORPHAN_WITHHOLDING\":\n return `Withholding row for ${w.isin} on ${w.date}: no matching income row — skipped`;\n }\n}\n","import { Transaction, IncomeRow } from \"../types.js\";\nimport { ParseError } from \"../errors.js\";\nimport {\n lookupRate,\n getActiveSnapshot,\n RatesSnapshot,\n} from \"../rates/index.js\";\nimport { WarningEntry, warningToEnglish } from \"./warnings.js\";\n\nconst REQUIRED_COLUMNS = [\n \"ISIN\",\n \"Quantity\",\n \"Price\",\n \"Date\",\n \"Local value\",\n \"Local value currency\",\n \"Transaction costs\",\n \"Transaction costs currency\",\n \"Product\",\n] as const;\n\n// Income-row product keyword matching (case-insensitive substring match).\n// Tax keywords are checked first because \"DIVIDEND TAX\" also contains \"DIVIDEND\".\nconst INCOME_KEYWORDS = [\"DIVIDEND\", \"COUPON\", \"INTEREST\", \"CEDOLA\"] as const;\nconst TAX_KEYWORDS = [\"DIVIDEND TAX\", \"WITHHOLDING\", \"RITENUTA\"] as const;\n\ninterface IncomeCandidate {\n isin: string;\n product: string;\n date: string;\n incomeType: \"dividend\" | \"coupon\";\n localValue: number;\n currency: string;\n row: number;\n}\n\ninterface WithholdingCandidate {\n isin: string;\n date: string;\n amount: number; // absolute value, in source currency\n row: number;\n}\n\n/**\n * Parse a single CSV line, respecting RFC-4180-style double-quote escaping.\n * Does not support embedded newlines in fields (not needed for DEGIRO exports).\n */\nfunction parseCSVRow(line: string): string[] {\n const fields: string[] = [];\n let i = 0;\n\n while (i <= line.length) {\n // End of line — stop (handles trailing comma by not emitting extra empty field)\n if (i === line.length) break;\n\n if (line[i] === '\"') {\n // Quoted field\n let field = \"\";\n i++; // skip opening quote\n while (i < line.length) {\n if (line[i] === '\"' && i + 1 < line.length && line[i + 1] === '\"') {\n // Escaped double-quote\n field += '\"';\n i += 2;\n } else if (line[i] === '\"') {\n i++; // skip closing quote\n break;\n } else {\n field += line[i++];\n }\n }\n fields.push(field);\n if (i < line.length && line[i] === \",\") i++; // skip delimiter\n } else {\n // Unquoted field\n const start = i;\n while (i < line.length && line[i] !== \",\") i++;\n fields.push(line.slice(start, i));\n if (i < line.length) i++; // skip delimiter\n }\n }\n\n return fields;\n}\n\n/**\n * Parse a full CSV string (CRLF or LF line endings) into a 2-D array of strings.\n */\nfunction parseCSV(input: string): string[][] {\n const normalized = input.replace(/\\r\\n/g, \"\\n\").replace(/\\r/g, \"\\n\");\n return normalized.split(\"\\n\").map(parseCSVRow);\n}\n\n/**\n * Convert a DEGIRO date \"DD-MM-YYYY\" to ISO format \"YYYY-MM-DD\".\n */\nfunction parseDate(ddmmyyyy: string): string {\n const [dd, mm, yyyy] = ddmmyyyy.split(\"-\");\n return `${yyyy}-${mm}-${dd}`;\n}\n\n/**\n * Parses a DEGIRO Transactions CSV export into normalised Transaction objects.\n *\n * Export from Activity → Transactions in the DEGIRO UI (not the Account Statement).\n * Supported currencies: EUR, USD, GBP, CHF. ECB historical rates are used for conversion.\n */\nexport class DEGIROParser {\n private _warningEntries: WarningEntry[] = [];\n private _snapshot?: RatesSnapshot;\n private _incomeRows: IncomeRow[] = [];\n\n constructor(snapshot?: RatesSnapshot) {\n this._snapshot = snapshot;\n }\n\n /**\n * Parse a DEGIRO Transactions CSV export string.\n *\n * @param csv - Raw UTF-8 string from the DEGIRO Transactions export.\n * @returns Array of normalised Transaction objects. Empty if no data rows are found.\n * @throws {ParseError} code `\"INVALID_CSV\"` — malformed CSV or binary content.\n * @throws {ParseError} code `\"MISSING_COLUMN\"` (+ `columnName`) — required column absent.\n *\n * Rows with missing ISIN, unsupported currency, zero quantity, or no ECB rate within\n * 3 trading days are skipped silently. Inspect `parser.warnings` for details.\n */\n parse(csv: string): Transaction[] {\n this._warningEntries = [];\n this._incomeRows = [];\n\n // Binary content (null bytes) is not valid CSV\n if (typeof csv !== \"string\" || csv.includes(\"\\x00\")) {\n throw new ParseError(\"INVALID_CSV\");\n }\n\n let rows: string[][];\n try {\n rows = parseCSV(csv);\n } catch {\n throw new ParseError(\"INVALID_CSV\");\n }\n\n if (rows.length === 0) {\n throw new ParseError(\"INVALID_CSV\");\n }\n\n // Build column-name → index map from the header row\n const header = rows[0];\n const colIndex: Record<string, number> = {};\n for (let i = 0; i < header.length; i++) {\n colIndex[header[i].trim()] = i;\n }\n\n // Validate that every required column is present\n for (const col of REQUIRED_COLUMNS) {\n if (colIndex[col] === undefined) {\n throw new ParseError(\"MISSING_COLUMN\", col);\n }\n }\n\n // Resolve the rates snapshot once for the whole parse run\n const snapshot: RatesSnapshot = this._snapshot ?? getActiveSnapshot();\n\n const transactions: Transaction[] = [];\n const incomeCandidates: IncomeCandidate[] = [];\n const withholdingCandidates: WithholdingCandidate[] = [];\n\n for (let r = 1; r < rows.length; r++) {\n const row = rows[r];\n // 1-indexed row number: header = 1, first data row = 2\n const rowIndex = r + 1;\n\n // Skip completely empty rows (including the trailing empty line after a\n // final newline, and rows that are all whitespace)\n if (row.every((cell) => cell.trim() === \"\")) continue;\n\n const get = (col: string): string => (row[colIndex[col]] ?? \"\").trim();\n\n const isin = get(\"ISIN\");\n const product = get(\"Product\");\n const qtyStr = get(\"Quantity\");\n const rawQty = parseFloat(qtyStr);\n const qtyIsZeroOrBlank = qtyStr === \"\" || isNaN(rawQty) || rawQty === 0;\n\n // --- Income-row detection (Quantity == 0 or blank) ---\n if (qtyIsZeroOrBlank) {\n const upperProduct = product.toUpperCase();\n const isTaxKeyword = TAX_KEYWORDS.some((k) => upperProduct.includes(k));\n const isIncomeKeyword =\n !isTaxKeyword &&\n INCOME_KEYWORDS.some((k) => upperProduct.includes(k));\n\n if (isTaxKeyword || isIncomeKeyword) {\n const rawLocalValue = parseFloat(get(\"Local value\"));\n const localValue = isNaN(rawLocalValue) ? 0 : rawLocalValue;\n const isoDate = parseDate(get(\"Date\"));\n\n if (isTaxKeyword && localValue < 0) {\n if (!isin) {\n this._warningEntries.push({\n code: \"MISSING_ISIN_INCOME\",\n row: rowIndex,\n });\n continue;\n }\n withholdingCandidates.push({\n isin,\n date: isoDate,\n amount: Math.abs(localValue),\n row: rowIndex,\n });\n continue;\n }\n\n if (isIncomeKeyword && localValue > 0) {\n if (!isin) {\n this._warningEntries.push({\n code: \"MISSING_ISIN_INCOME\",\n row: rowIndex,\n });\n continue;\n }\n const incomeType: \"dividend\" | \"coupon\" = upperProduct.includes(\n \"DIVIDEND\",\n )\n ? \"dividend\"\n : \"coupon\";\n incomeCandidates.push({\n isin,\n product,\n date: isoDate,\n incomeType,\n localValue,\n currency: get(\"Local value currency\"),\n row: rowIndex,\n });\n continue;\n }\n // Keyword matched but sign doesn't match the expected direction —\n // fall through to standard transaction handling below.\n }\n }\n\n // --- ISIN ---\n if (!isin) {\n this._warningEntries.push({ code: \"MISSING_ISIN\", row: rowIndex });\n continue;\n }\n\n // --- Quantity ---\n if (isNaN(rawQty) || rawQty === 0) {\n this._warningEntries.push({ code: \"QUANTITY_ZERO\", row: rowIndex });\n continue;\n }\n\n const type: \"BUY\" | \"SELL\" = rawQty > 0 ? \"BUY\" : \"SELL\";\n const quantity = Math.abs(rawQty);\n\n // --- Date ---\n const isoDate = parseDate(get(\"Date\"));\n\n // --- Currency & FX ---\n const currency = get(\"Local value currency\");\n const rawTotalLocal = parseFloat(get(\"Local value\"));\n const totalLocal = isNaN(rawTotalLocal) ? 0 : rawTotalLocal;\n\n let totalEUR: number;\n let fxRate: number | undefined;\n\n if (currency === \"EUR\") {\n totalEUR = Math.abs(totalLocal);\n fxRate = undefined;\n } else {\n const rate = lookupRate(currency, isoDate, snapshot);\n if (rate === null) {\n if (snapshot[currency] === undefined) {\n this._warningEntries.push({\n code: \"UNSUPPORTED_CURRENCY\",\n row: rowIndex,\n currency,\n });\n } else {\n this._warningEntries.push({\n code: \"NO_ECB_RATE\",\n row: rowIndex,\n currency,\n date: isoDate,\n });\n }\n continue;\n }\n totalEUR = Math.abs(totalLocal) / rate;\n fxRate = rate;\n }\n\n // --- Fees ---\n const rawFees = parseFloat(get(\"Transaction costs\"));\n const feesEUR = Math.abs(isNaN(rawFees) ? 0 : rawFees);\n\n // --- Remaining fields ---\n const pricePerUnit = parseFloat(get(\"Price\"));\n\n transactions.push({\n isin,\n product,\n date: isoDate,\n type,\n quantity,\n pricePerUnit,\n currency,\n totalLocal,\n totalEUR,\n feesEUR,\n fxRate,\n });\n }\n\n // --- Pair withholding candidates with income candidates by (ISIN, date) ---\n // Order-independent: candidates were collected regardless of CSV row order.\n const withholdingByKey = new Map<\n string,\n { isin: string; date: string; amount: number }\n >();\n for (const w of withholdingCandidates) {\n const key = `${w.isin}|${w.date}`;\n const existing = withholdingByKey.get(key);\n withholdingByKey.set(key, {\n isin: w.isin,\n date: w.date,\n amount: (existing?.amount ?? 0) + w.amount,\n });\n }\n const consumedKeys = new Set<string>();\n\n const incomeRows: IncomeRow[] = [];\n for (const c of incomeCandidates) {\n const key = `${c.isin}|${c.date}`;\n const matchedWithholding = withholdingByKey.get(key);\n\n let grossAmount: number;\n let withholdingTax: number;\n let fxRate: number | undefined;\n\n if (c.currency === \"EUR\") {\n grossAmount = Math.abs(c.localValue);\n withholdingTax = matchedWithholding?.amount ?? 0;\n fxRate = undefined;\n } else {\n const rate = lookupRate(c.currency, c.date, snapshot);\n if (rate === null) {\n if (snapshot[c.currency] === undefined) {\n this._warningEntries.push({\n code: \"UNSUPPORTED_CURRENCY\",\n row: c.row,\n currency: c.currency,\n });\n } else {\n this._warningEntries.push({\n code: \"NO_ECB_RATE\",\n row: c.row,\n currency: c.currency,\n date: c.date,\n });\n }\n continue;\n }\n grossAmount = Math.abs(c.localValue) / rate;\n withholdingTax = matchedWithholding\n ? matchedWithholding.amount / rate\n : 0;\n fxRate = rate;\n }\n\n if (matchedWithholding) consumedKeys.add(key);\n\n incomeRows.push({\n isin: c.isin,\n product: c.product,\n date: c.date,\n incomeType: c.incomeType,\n grossAmount,\n withholdingTax,\n currency: c.currency,\n fxRate,\n });\n }\n\n // Orphan withholding candidates — no matching income row for (ISIN, date)\n for (const [key, w] of withholdingByKey) {\n if (!consumedKeys.has(key)) {\n this._warningEntries.push({\n code: \"ORPHAN_WITHHOLDING\",\n isin: w.isin,\n date: w.date,\n });\n }\n }\n\n this._incomeRows = incomeRows;\n\n return transactions;\n }\n\n get warnings(): string[] {\n return this._warningEntries.map(warningToEnglish);\n }\n\n get warningEntries(): WarningEntry[] {\n return this._warningEntries;\n }\n\n get incomeRows(): IncomeRow[] {\n return this._incomeRows;\n }\n}\n","import { writeFile } from \"node:fs/promises\";\nimport type {\n BucketAReport,\n BucketBReport,\n CarryForward,\n CarryForwardEntry,\n DichiarazioneReport,\n IncomeRow,\n QuadroRMReport,\n QuadroRTReport,\n} from \"../types.js\";\n\nfunction roundHalfUp(x: number): number {\n return (Math.sign(x) * Math.round(Math.abs(x) * 100)) / 100;\n}\n\nexport function buildQuadroRT(\n bucketB: BucketBReport,\n carryForward: CarryForward[],\n taxYear: number,\n): QuadroRTReport {\n const plusvalenze = bucketB.plusvalenze;\n const minusvalenze = bucketB.minusvalenze;\n const differenza = roundHalfUp(plusvalenze - minusvalenze);\n\n if (differenza > 0) {\n const sorted = [...carryForward].sort((a, b) => a.year - b.year);\n let remaining = differenza;\n const carryForwardApplied: CarryForwardEntry[] = [];\n\n for (const entry of sorted) {\n if (taxYear - entry.year > 4) continue;\n const consumed = Math.min(entry.amount, remaining);\n if (consumed > 0) {\n carryForwardApplied.push({\n annoOrigine: entry.year,\n importo: roundHalfUp(consumed),\n });\n remaining -= consumed;\n }\n }\n\n const imponibileNetto = roundHalfUp(remaining);\n const imposta = roundHalfUp(imponibileNetto * 0.26);\n\n return {\n plusvalenze,\n minusvalenze,\n differenza,\n carryForwardApplied,\n imponibileNetto,\n imposta,\n carryForwardRiportato: [],\n };\n } else if (differenza < 0) {\n return {\n plusvalenze,\n minusvalenze,\n differenza,\n carryForwardApplied: [],\n imponibileNetto: 0,\n imposta: 0,\n carryForwardRiportato: [\n { annoOrigine: taxYear, importo: roundHalfUp(Math.abs(differenza)) },\n ],\n };\n } else {\n return {\n plusvalenze,\n minusvalenze,\n differenza: 0,\n carryForwardApplied: [],\n imponibileNetto: 0,\n imposta: 0,\n carryForwardRiportato: [],\n };\n }\n}\n\nexport function buildQuadroRM(\n bucketA: BucketAReport | undefined,\n incomeRows: IncomeRow[],\n taxYear: number,\n): QuadroRMReport {\n const groups = bucketA?.groups ?? [];\n\n const group26 = groups.find((g) => g.taxRate === 0.26);\n const group125 = groups.find((g) => g.taxRate === 0.125);\n\n const capitaleAliquota26 = {\n plusvalenze: group26?.plusvalenze ?? 0,\n imposta: group26?.imposta ?? 0,\n };\n const capitaleAliquota125 = {\n plusvalenze: group125?.plusvalenze ?? 0,\n imposta: group125?.imposta ?? 0,\n };\n\n const dividendiEsteri = incomeRows\n .filter((r) => r.incomeType === \"dividend\")\n .map((r) => ({\n isin: r.isin,\n prodotto: r.product,\n lordo: r.grossAmount,\n rittenutaEstera: r.withholdingTax,\n }));\n\n const cedole = incomeRows\n .filter((r) => r.incomeType === \"coupon\")\n .map((r) => ({\n isin: r.isin,\n prodotto: r.product,\n importo: r.grossAmount,\n rittenutaEstera: r.withholdingTax,\n }));\n\n return {\n capitaleAliquota26,\n capitaleAliquota125,\n dividendiEsteri,\n cedole,\n };\n}\n\nexport function buildDichiarazioneReport(\n quadroRT: QuadroRTReport,\n quadroRM: QuadroRMReport,\n taxYear: number,\n): DichiarazioneReport {\n const version = 1;\n const modello = \"Redditi PF\" as const;\n const generatedAt = new Date().toISOString();\n\n return {\n version,\n annoImposta: taxYear,\n modello,\n generatedAt,\n quadroRT,\n quadroRM,\n exportTo: async (path: string) => {\n await writeFile(\n path,\n JSON.stringify(\n {\n version,\n annoImposta: taxYear,\n modello,\n generatedAt,\n quadroRT,\n quadroRM,\n },\n null,\n 2,\n ),\n );\n },\n };\n}\n","import type {\n Transaction,\n MatchedLot,\n GainsReport,\n LotMethod,\n CalculatorOptions,\n AssetClass,\n BucketAReport,\n BucketBReport,\n} from \"../types.js\";\nimport { CalculationError } from \"../errors.js\";\nimport {\n buildQuadroRT,\n buildQuadroRM,\n buildDichiarazioneReport,\n} from \"../dichiarazione/engine.js\";\n\ninterface Lot {\n date: string;\n quantity: number;\n pricePerUnitEUR: number;\n feesEUR: number;\n originalQty: number;\n fxRate?: number;\n}\n\nfunction roundHalfUp(x: number): number {\n return (Math.sign(x) * Math.round(Math.abs(x) * 100)) / 100;\n}\n\nfunction inferTaxYear(transactions: Transaction[]): {\n year: number;\n multipleYears: boolean;\n} {\n const counts: Record<string, number> = {};\n for (const t of transactions) {\n const y = t.date.slice(0, 4);\n counts[y] = (counts[y] ?? 0) + 1;\n }\n const years = Object.keys(counts);\n if (years.length === 0)\n return { year: new Date().getFullYear(), multipleYears: false };\n const year = parseInt(\n years.reduce((a, b) => (counts[a] >= counts[b] ? a : b)),\n );\n return { year, multipleYears: years.length > 1 };\n}\n\n/**\n * Computes Italian capital gains and losses from a list of parsed transactions.\n *\n * Uses LIFO or FIFO lot matching and converts all amounts to EUR via ECB rates.\n */\nexport class Calculator {\n private readonly _transactions: Transaction[];\n private readonly _parseWarnings: string[];\n private readonly _options: CalculatorOptions;\n\n constructor(\n transactions: Transaction[],\n parseWarnings?: string[],\n options?: CalculatorOptions,\n ) {\n this._transactions = transactions;\n this._parseWarnings = parseWarnings ?? [];\n this._options = options ?? {};\n }\n\n /**\n * Run LIFO or FIFO lot-matching over the transaction list.\n *\n * @param method - `\"LIFO\"` or `\"FIFO\"`.\n * @returns GainsReport with `plusvalenze`, `minusvalenze`, `netResult`, per-lot breakdown,\n * ECB rates used, and any accumulated warnings.\n * @throws {CalculationError} when a SELL has no matching open buy lots.\n * `error.isin` and `error.date` identify the problematic transaction.\n */\n calculateGains(method: LotMethod): GainsReport {\n const warnings: string[] = [...this._parseWarnings];\n\n // 1. Sort ascending by date; BUY before SELL on same date\n const sorted = [...this._transactions].sort((a, b) => {\n if (a.date < b.date) return -1;\n if (a.date > b.date) return 1;\n // Same date: BUY before SELL\n if (a.type === \"BUY\" && b.type === \"SELL\") return -1;\n if (a.type === \"SELL\" && b.type === \"BUY\") return 1;\n return 0;\n });\n\n // 2. Tax year inference\n const { year: taxYear, multipleYears } = inferTaxYear(sorted);\n if (multipleYears) {\n warnings.push(\n \"CSV contains transactions from multiple years — filter to a single year for accurate reporting.\",\n );\n }\n\n // 3. Lot matching\n const openLots = new Map<string, Lot[]>();\n const matchedLots: MatchedLot[] = [];\n const ratesUsed: Record<string, number> = {};\n\n for (const tx of sorted) {\n // Collect rates used\n if (tx.fxRate !== undefined) {\n ratesUsed[`${tx.currency}:${tx.date}`] = tx.fxRate;\n }\n\n if (tx.type === \"BUY\") {\n const lot: Lot = {\n date: tx.date,\n quantity: tx.quantity,\n pricePerUnitEUR: tx.totalEUR / tx.quantity,\n feesEUR: tx.feesEUR,\n originalQty: tx.quantity,\n fxRate: tx.fxRate,\n };\n if (!openLots.has(tx.isin)) openLots.set(tx.isin, []);\n openLots.get(tx.isin)!.push(lot);\n } else {\n // SELL\n const lots = openLots.get(tx.isin);\n if (!lots || lots.length === 0) {\n throw new CalculationError(tx.isin, tx.date);\n }\n\n const sellPricePerUnitEUR = tx.totalEUR / tx.quantity;\n let remainingSellQty = tx.quantity;\n\n while (remainingSellQty > 0) {\n if (!lots || lots.length === 0) {\n throw new CalculationError(tx.isin, tx.date);\n }\n\n const lot = method === \"LIFO\" ? lots[lots.length - 1] : lots[0];\n const matchedQty = Math.min(lot.quantity, remainingSellQty);\n\n // Fee allocation\n const allocatedBuyFeesEUR =\n lot.feesEUR * (matchedQty / lot.originalQty);\n const allocatedSellFeesEUR = tx.feesEUR * (matchedQty / tx.quantity);\n\n const buyCostEUR =\n lot.pricePerUnitEUR * matchedQty + allocatedBuyFeesEUR;\n const sellProceedsEUR =\n sellPricePerUnitEUR * matchedQty - allocatedSellFeesEUR;\n const gainLossEUR = sellProceedsEUR - buyCostEUR;\n\n matchedLots.push({\n isin: tx.isin,\n product: tx.product,\n quantity: matchedQty,\n buyDate: lot.date,\n sellDate: tx.date,\n buyPriceEUR: lot.pricePerUnitEUR,\n sellPriceEUR: sellPricePerUnitEUR,\n buyCostEUR: roundHalfUp(buyCostEUR),\n sellProceedsEUR: roundHalfUp(sellProceedsEUR),\n gainLossEUR: roundHalfUp(gainLossEUR),\n buyFxRate: lot.fxRate,\n sellFxRate: tx.fxRate,\n });\n\n lot.quantity -= matchedQty;\n remainingSellQty -= matchedQty;\n\n if (lot.quantity <= 0) {\n if (method === \"LIFO\") {\n lots.pop();\n } else {\n lots.shift();\n }\n }\n }\n }\n }\n\n // 4. Aggregate\n let plusvalenze = 0;\n let minusvalenze = 0;\n for (const lot of matchedLots) {\n if (lot.gainLossEUR > 0) plusvalenze += lot.gainLossEUR;\n else minusvalenze += Math.abs(lot.gainLossEUR);\n }\n\n // Two-bucket routing (only when classification map provided)\n if (this._options.classification) {\n const classification = this._options.classification;\n const unclassifiedIsins = new Set<string>();\n\n for (const lot of matchedLots) {\n const entry = classification[lot.isin];\n if (!entry) {\n lot.bucket = \"B\";\n unclassifiedIsins.add(lot.isin);\n } else if (entry.bucketGain === \"A\" && lot.gainLossEUR >= 0) {\n lot.bucket = \"A\";\n } else {\n lot.bucket = \"B\";\n }\n }\n\n for (const isin of unclassifiedIsins) {\n warnings.push(\n `ISIN ${isin} not found in classification map — assigned to Bucket B.`,\n );\n }\n\n // Bucket A computation\n const bucketALots = matchedLots.filter((l) => l.bucket === \"A\");\n const groupsByRate = new Map<\n number,\n { assetClasses: Set<string>; plusvalenze: number }\n >();\n for (const lot of bucketALots) {\n const entry = classification[lot.isin]!;\n const rate = entry.taxRate;\n if (!groupsByRate.has(rate))\n groupsByRate.set(rate, { assetClasses: new Set(), plusvalenze: 0 });\n const g = groupsByRate.get(rate)!;\n g.assetClasses.add(entry.assetClass);\n g.plusvalenze += lot.gainLossEUR;\n }\n const bucketAGroups = [...groupsByRate.entries()].map(([taxRate, g]) => ({\n taxRate,\n assetClasses: [...g.assetClasses] as AssetClass[],\n plusvalenze: roundHalfUp(g.plusvalenze),\n imposta: roundHalfUp(g.plusvalenze * taxRate),\n }));\n const bucketAReport: BucketAReport = {\n groups: bucketAGroups,\n totalImposta: roundHalfUp(\n bucketAGroups.reduce((s, g) => s + g.imposta, 0),\n ),\n };\n\n // Bucket B computation\n const bucketBLots = matchedLots.filter((l) => l.bucket === \"B\");\n let bPlusvalenze = 0;\n let bMinusvalenze = 0;\n for (const lot of bucketBLots) {\n if (lot.gainLossEUR >= 0) bPlusvalenze += lot.gainLossEUR;\n else bMinusvalenze += Math.abs(lot.gainLossEUR);\n }\n bPlusvalenze = roundHalfUp(bPlusvalenze);\n bMinusvalenze = roundHalfUp(bMinusvalenze);\n\n const carryForwards = [...(this._options.carryForward ?? [])].sort(\n (a, b) => a.year - b.year,\n );\n let remaining = bPlusvalenze - bMinusvalenze;\n let carryForwardApplied = 0;\n for (const entry of carryForwards) {\n if (taxYear - entry.year > 4) continue;\n if (remaining <= 0) break;\n const consumed = Math.min(entry.amount, remaining);\n carryForwardApplied += consumed;\n remaining -= consumed;\n }\n carryForwardApplied = roundHalfUp(carryForwardApplied);\n const bNetResult = roundHalfUp(\n bPlusvalenze - bMinusvalenze - carryForwardApplied,\n );\n const carryForwardRemaining = roundHalfUp(Math.max(0, -bNetResult));\n\n const bucketBReport: BucketBReport = {\n plusvalenze: bPlusvalenze,\n minusvalenze: bMinusvalenze,\n carryForwardApplied,\n carryForwardRemaining,\n netResult: bNetResult,\n };\n\n // Filter income rows to the tax year and build the dichiarazione report\n const allIncomeRows = this._options.incomeRows ?? [];\n const filteredIncomeRows = allIncomeRows.filter(\n (row) => new Date(row.date).getFullYear() === taxYear,\n );\n if (filteredIncomeRows.length < allIncomeRows.length) {\n warnings.push(`Income rows outside tax year ${taxYear} were skipped.`);\n }\n\n const quadroRT = buildQuadroRT(\n bucketBReport,\n this._options.carryForward ?? [],\n taxYear,\n );\n const quadroRM = buildQuadroRM(\n bucketAGroups.length > 0 ? bucketAReport : undefined,\n filteredIncomeRows,\n taxYear,\n );\n const dichiarazioneReport = buildDichiarazioneReport(\n quadroRT,\n quadroRM,\n taxYear,\n );\n\n return {\n method,\n taxYear,\n plusvalenze: roundHalfUp(plusvalenze),\n minusvalenze: roundHalfUp(minusvalenze),\n netResult: roundHalfUp(plusvalenze - minusvalenze),\n lots: matchedLots,\n ratesUsed,\n warnings,\n generatedAt: new Date().toISOString(),\n bucketA: bucketAGroups.length > 0 ? bucketAReport : undefined,\n bucketB: bucketBReport,\n dichiarazione: dichiarazioneReport,\n };\n } else {\n // Mixed-asset heuristic (no classification map)\n const allIsins = [...new Set(this._transactions.map((t) => t.isin))];\n const hasIePrefix = allIsins.some((isin) => isin.startsWith(\"IE\"));\n const hasOther = allIsins.some((isin) => !isin.startsWith(\"IE\"));\n if (hasIePrefix && hasOther) {\n warnings.push(\n \"AVVISO: CSV contiene tipi di strumenti misti (es. ETF + Azioni).\\n\" +\n \" Il calcolo a bucket unico può non essere fiscalmente corretto.\\n\" +\n \" Esegui: minus-tracker classify trades.csv\",\n );\n }\n }\n\n return {\n method,\n taxYear,\n plusvalenze: roundHalfUp(plusvalenze),\n minusvalenze: roundHalfUp(minusvalenze),\n netResult: roundHalfUp(plusvalenze - minusvalenze),\n lots: matchedLots,\n ratesUsed,\n warnings,\n generatedAt: new Date().toISOString(),\n };\n }\n}\n","import { ClassificationError } from \"../errors.js\";\nimport type {\n AssetClass,\n ClassificationEntry,\n ClassificationMap,\n ClassifyOptions,\n Transaction,\n} from \"../types.js\";\nimport * as fs from \"node:fs\";\nimport * as https from \"node:https\";\n\ntype HttpPost = (\n url: string,\n body: string,\n timeoutMs: number,\n) => Promise<{ status: number; data: string }>;\n\nfunction httpsPost(\n url: string,\n body: string,\n timeoutMs: number,\n): Promise<{ status: number; data: string }> {\n return new Promise((resolve, reject) => {\n const u = new URL(url);\n const req = https.request(\n {\n hostname: u.hostname,\n path: u.pathname + u.search,\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"Content-Length\": Buffer.byteLength(body),\n },\n },\n (res) => {\n let data = \"\";\n res.on(\"data\", (chunk) => {\n data += chunk;\n });\n res.on(\"end\", () => resolve({ status: res.statusCode ?? 0, data }));\n },\n );\n req.setTimeout(timeoutMs, () => {\n req.destroy();\n reject(new Error(\"timeout\"));\n });\n req.on(\"error\", reject);\n req.write(body);\n req.end();\n });\n}\n\nconst GOVT_BOND_WHITELIST = new Set([\n \"IT\",\n \"DE\",\n \"FR\",\n \"AT\",\n \"BE\",\n \"NL\",\n \"ES\",\n \"PT\",\n \"FI\",\n \"IE\",\n \"LU\",\n \"GR\",\n \"SK\",\n \"SI\",\n \"LT\",\n \"LV\",\n \"EE\",\n \"MT\",\n \"CY\",\n \"US\",\n \"GB\",\n \"CH\",\n \"NO\",\n \"SE\",\n \"DK\",\n \"JP\",\n \"CA\",\n \"AU\",\n \"NZ\",\n \"SG\",\n \"HK\",\n \"KR\",\n]);\n\ntype TypeMapping = {\n assetClass: AssetClass;\n bucketGain: \"A\" | \"B\";\n bucketLoss: \"A\" | \"B\";\n taxRate: number;\n whiteListed: boolean | null;\n};\n\nconst SECURITY_TYPE_MAP: Record<string, TypeMapping> = {\n ETP: {\n assetClass: \"ETF\",\n bucketGain: \"A\",\n bucketLoss: \"B\",\n taxRate: 0.26,\n whiteListed: null,\n },\n ETF: {\n assetClass: \"ETF\",\n bucketGain: \"A\",\n bucketLoss: \"B\",\n taxRate: 0.26,\n whiteListed: null,\n },\n \"Mutual Fund\": {\n assetClass: \"ETF\",\n bucketGain: \"A\",\n bucketLoss: \"B\",\n taxRate: 0.26,\n whiteListed: null,\n },\n \"Open-End Fund\": {\n assetClass: \"ETF\",\n bucketGain: \"A\",\n bucketLoss: \"B\",\n taxRate: 0.26,\n whiteListed: null,\n },\n \"Common Stock\": {\n assetClass: \"Stock\",\n bucketGain: \"B\",\n bucketLoss: \"B\",\n taxRate: 0,\n whiteListed: null,\n },\n Equity: {\n assetClass: \"Stock\",\n bucketGain: \"B\",\n bucketLoss: \"B\",\n taxRate: 0,\n whiteListed: null,\n },\n \"Exchange Traded Commodity\": {\n assetClass: \"ETC\",\n bucketGain: \"B\",\n bucketLoss: \"B\",\n taxRate: 0,\n whiteListed: null,\n },\n \"Corporate Bond\": {\n assetClass: \"CorpBond\",\n bucketGain: \"B\",\n bucketLoss: \"B\",\n taxRate: 0,\n whiteListed: null,\n },\n Option: {\n assetClass: \"Derivative\",\n bucketGain: \"B\",\n bucketLoss: \"B\",\n taxRate: 0,\n whiteListed: null,\n },\n Future: {\n assetClass: \"Derivative\",\n bucketGain: \"B\",\n bucketLoss: \"B\",\n taxRate: 0,\n whiteListed: null,\n },\n Warrant: {\n assetClass: \"Derivative\",\n bucketGain: \"B\",\n bucketLoss: \"B\",\n taxRate: 0,\n whiteListed: null,\n },\n CFD: {\n assetClass: \"Derivative\",\n bucketGain: \"B\",\n bucketLoss: \"B\",\n taxRate: 0,\n whiteListed: null,\n },\n Swap: {\n assetClass: \"Derivative\",\n bucketGain: \"B\",\n bucketLoss: \"B\",\n taxRate: 0,\n whiteListed: null,\n },\n \"Leverage Certificate\": {\n assetClass: \"LeverageCert\",\n bucketGain: \"B\",\n bucketLoss: \"B\",\n taxRate: 0,\n whiteListed: null,\n },\n \"Turbo Certificate\": {\n assetClass: \"LeverageCert\",\n bucketGain: \"B\",\n bucketLoss: \"B\",\n taxRate: 0,\n whiteListed: null,\n },\n \"Capital Protected Certificate\": {\n assetClass: \"CapProtectedCert\",\n bucketGain: \"A\",\n bucketLoss: \"B\",\n taxRate: 0.26,\n whiteListed: null,\n },\n};\n\nfunction isKnownType(t: string): boolean {\n return t === \"Government Bond\" || t in SECURITY_TYPE_MAP;\n}\n\nfunction classifyByType(\n isin: string,\n securityType: string,\n product: string,\n warnings: string[],\n): ClassificationEntry {\n if (securityType === \"Government Bond\") {\n const prefix = isin.slice(0, 2).toUpperCase();\n const whiteListed = GOVT_BOND_WHITELIST.has(prefix);\n return {\n product,\n assetClass: whiteListed ? \"GovtBondWL\" : \"GovtBondOther\",\n bucketGain: \"A\",\n bucketLoss: \"B\",\n taxRate: whiteListed ? 0.125 : 0.26,\n whiteListed,\n confirmedByUser: false,\n source: \"openfigi\",\n };\n }\n\n const mapped = SECURITY_TYPE_MAP[securityType];\n if (mapped) {\n return {\n product,\n assetClass: mapped.assetClass,\n bucketGain: mapped.bucketGain,\n bucketLoss: mapped.bucketLoss,\n taxRate: mapped.taxRate,\n whiteListed: mapped.whiteListed,\n confirmedByUser: false,\n source: \"openfigi\",\n };\n }\n\n // Unknown type — flag for manual review\n warnings.push(\n `Unrecognized type for ${isin}: ${securityType}. Please classify manually.`,\n );\n return {\n product,\n assetClass: \"Stock\",\n bucketGain: \"B\",\n bucketLoss: \"B\",\n taxRate: 0,\n whiteListed: null,\n confirmedByUser: false,\n source: \"user\",\n };\n}\n\nconst ASSET_CLASS_DEFAULTS: Record<AssetClass, TypeMapping> = {\n ETF: {\n assetClass: \"ETF\",\n bucketGain: \"A\",\n bucketLoss: \"B\",\n taxRate: 0.26,\n whiteListed: null,\n },\n Stock: {\n assetClass: \"Stock\",\n bucketGain: \"B\",\n bucketLoss: \"B\",\n taxRate: 0,\n whiteListed: null,\n },\n ETC: {\n assetClass: \"ETC\",\n bucketGain: \"B\",\n bucketLoss: \"B\",\n taxRate: 0,\n whiteListed: null,\n },\n GovtBondWL: {\n assetClass: \"GovtBondWL\",\n bucketGain: \"A\",\n bucketLoss: \"B\",\n taxRate: 0.125,\n whiteListed: true,\n },\n GovtBondOther: {\n assetClass: \"GovtBondOther\",\n bucketGain: \"A\",\n bucketLoss: \"B\",\n taxRate: 0.26,\n whiteListed: false,\n },\n CorpBond: {\n assetClass: \"CorpBond\",\n bucketGain: \"B\",\n bucketLoss: \"B\",\n taxRate: 0,\n whiteListed: null,\n },\n Derivative: {\n assetClass: \"Derivative\",\n bucketGain: \"B\",\n bucketLoss: \"B\",\n taxRate: 0,\n whiteListed: null,\n },\n LeverageCert: {\n assetClass: \"LeverageCert\",\n bucketGain: \"B\",\n bucketLoss: \"B\",\n taxRate: 0,\n whiteListed: null,\n },\n CapProtectedCert: {\n assetClass: \"CapProtectedCert\",\n bucketGain: \"A\",\n bucketLoss: \"B\",\n taxRate: 0.26,\n whiteListed: null,\n },\n};\n\n/**\n * Builds a user-confirmed `ClassificationEntry` from a caller-supplied\n * `AssetClass` override (v0.8.0 stateless mode). The caller picked the asset\n * class directly, so no ISIN-prefix whitelist lookup is performed here — the\n * caller owns gov-bond-whitelisted-vs-other correctness.\n */\nfunction buildEntryFromAssetClass(\n assetClass: AssetClass,\n product: string,\n): ClassificationEntry {\n const mapped = ASSET_CLASS_DEFAULTS[assetClass];\n return {\n product,\n assetClass: mapped.assetClass,\n bucketGain: mapped.bucketGain,\n bucketLoss: mapped.bucketLoss,\n taxRate: mapped.taxRate,\n whiteListed: mapped.whiteListed,\n confirmedByUser: true,\n source: \"user\",\n };\n}\n\ntype OpenFIGIItem = { securityType?: string; securityType2?: string };\ntype OpenFIGIResult = { data?: OpenFIGIItem[]; error?: string };\n\nexport class Classifier {\n private readonly interactive: boolean;\n\n constructor(options?: { interactive?: boolean }) {\n this.interactive = options?.interactive ?? true;\n }\n\n async load(sidecarPath: string): Promise<ClassificationMap> {\n if (!fs.existsSync(sidecarPath)) {\n throw new ClassificationError(\"SIDECAR_NOT_FOUND\");\n }\n\n let parsed: unknown;\n try {\n const raw = fs.readFileSync(sidecarPath, \"utf-8\");\n parsed = JSON.parse(raw);\n } catch {\n throw new ClassificationError(\"SIDECAR_MALFORMED\");\n }\n\n if (\n typeof parsed !== \"object\" ||\n parsed === null ||\n (parsed as Record<string, unknown>)[\"version\"] !== 1\n ) {\n throw new ClassificationError(\"SIDECAR_VERSION\");\n }\n\n return (parsed as { classifications: ClassificationMap }).classifications;\n }\n\n async classify(\n transactions: Transaction[],\n sidecarPath?: string,\n options?: ClassifyOptions,\n _httpPost: HttpPost = httpsPost,\n ): Promise<ClassificationMap> {\n // 1. Deduplicate ISINs; track first product name seen per ISIN\n const isinToProduct = new Map<string, string>();\n for (const tx of transactions) {\n if (tx.isin && !isinToProduct.has(tx.isin)) {\n isinToProduct.set(tx.isin, tx.product);\n }\n }\n\n // 2. Build the \"already resolved\" set.\n // Sidecar mode (unchanged v0.6.0 behavior): only confirmedByUser:true\n // entries from disk are trusted; everything else is re-processed.\n // Stateless mode (sidecarPath undefined, v0.8.0): no disk access at\n // all — ALL entries in options.existingClassification are trusted\n // as-is, regardless of confirmedByUser.\n const confirmed: ClassificationMap = {};\n\n if (sidecarPath !== undefined) {\n if (fs.existsSync(sidecarPath)) {\n const existingMap = await this.load(sidecarPath);\n for (const [isin, entry] of Object.entries(existingMap)) {\n if (entry.confirmedByUser) {\n confirmed[isin] = entry;\n }\n }\n }\n } else if (options?.existingClassification) {\n Object.assign(confirmed, options.existingClassification);\n }\n\n // overrides always win, in both modes, and are never sent to OpenFIGI\n for (const [isin, assetClass] of Object.entries(options?.overrides ?? {})) {\n const product = isinToProduct.get(isin) ?? isin;\n confirmed[isin] = buildEntryFromAssetClass(assetClass, product);\n }\n\n // 3. ISINs that need (re-)processing: not already resolved\n const toProcess: string[] = [];\n for (const isin of isinToProduct.keys()) {\n if (!(isin in confirmed)) {\n toProcess.push(isin);\n }\n }\n\n // 4. Batch-query OpenFIGI (skipped entirely in offline mode)\n const warnings: string[] = [];\n const newEntries: ClassificationMap = {};\n\n if (options?.offline) {\n for (const isin of toProcess) {\n const product = isinToProduct.get(isin) ?? isin;\n warnings.push(\n `Unrecognized type for ${isin}: unknown. Please classify manually.`,\n );\n newEntries[isin] = {\n product,\n assetClass: \"Stock\",\n bucketGain: \"B\",\n bucketLoss: \"B\",\n taxRate: 0,\n whiteListed: null,\n confirmedByUser: false,\n source: \"user\",\n };\n }\n } else {\n const BATCH_SIZE = 10;\n const totalBatches = Math.ceil(toProcess.length / BATCH_SIZE);\n\n for (let i = 0; i < toProcess.length; i += BATCH_SIZE) {\n // Wait 6 s between consecutive batch requests\n if (i > 0) {\n await new Promise<void>((resolve) => setTimeout(resolve, 6000));\n }\n\n const batch = toProcess.slice(i, i + BATCH_SIZE);\n const requestBody = JSON.stringify(\n batch.map((isin) => ({ idType: \"ID_ISIN\", idValue: isin })),\n );\n\n let response: { status: number; data: string } | null = null;\n\n // Try once; retry once on 5xx\n for (let attempt = 0; attempt < 2; attempt++) {\n try {\n const r = await _httpPost(\n \"https://api.openfigi.com/v3/mapping\",\n requestBody,\n 10_000,\n );\n if (r.status < 500) {\n response = r;\n break;\n }\n // 5xx — loop will retry once\n } catch {\n // Network-level error — no retry\n throw new ClassificationError(\"NETWORK_ERROR\");\n }\n }\n\n if (response === null) {\n throw new ClassificationError(\"NETWORK_ERROR\");\n }\n\n const results = JSON.parse(response.data) as OpenFIGIResult[];\n\n for (let j = 0; j < batch.length; j++) {\n const isin = batch[j]!;\n const result = results[j];\n const product = isinToProduct.get(isin) ?? isin;\n\n if (\n !result ||\n result.error ||\n !result.data ||\n result.data.length === 0\n ) {\n warnings.push(\n `Unrecognized type for ${isin}: unknown. Please classify manually.`,\n );\n newEntries[isin] = {\n product,\n assetClass: \"Stock\",\n bucketGain: \"B\",\n bucketLoss: \"B\",\n taxRate: 0,\n whiteListed: null,\n confirmedByUser: false,\n source: \"user\",\n };\n continue;\n }\n\n const st = result.data[0]?.securityType;\n const st2 = result.data[0]?.securityType2;\n\n // Prefer the type that resolves to a known mapping\n const typeToUse =\n st && isKnownType(st)\n ? st\n : st2 && isKnownType(st2)\n ? st2\n : (st ?? st2 ?? \"Unknown\");\n\n newEntries[isin] = classifyByType(isin, typeToUse, product, warnings);\n }\n\n const batchIndex = i / BATCH_SIZE;\n options?.onBatchProgress?.(batchIndex + 1, totalBatches);\n }\n }\n\n // 5. Merge: confirmed/overrides entries take precedence and remain untouched\n const mergedMap: ClassificationMap = { ...newEntries, ...confirmed };\n\n // 6. Write sidecar — only in sidecar mode; stateless mode never touches disk\n if (sidecarPath !== undefined) {\n const sidecarContent = JSON.stringify(\n {\n version: 1,\n generatedAt: new Date().toISOString(),\n classifications: mergedMap,\n warnings,\n },\n null,\n 2,\n );\n\n try {\n fs.writeFileSync(sidecarPath, sidecarContent, \"utf-8\");\n } catch {\n throw new ClassificationError(\"WRITE_ERROR\");\n }\n }\n\n return mergedMap;\n }\n}\n","import type { GainsReport, AssetClass } from \"../types.js\";\nimport type { LocaleStrings } from \"../i18n/types.js\";\n\nconst SEPARATOR = \"─\".repeat(72);\n\nfunction formatEUR(amount: number, locale: string): string {\n return new Intl.NumberFormat(locale, {\n minimumFractionDigits: 2,\n maximumFractionDigits: 2,\n }).format(amount);\n}\n\nfunction formatGainLoss(amount: number, locale: string): string {\n const formatted = formatEUR(Math.abs(amount), locale);\n return amount >= 0 ? `+${formatted}` : `-${formatted}`;\n}\n\nfunction assetClassLabel(ac: AssetClass, s: LocaleStrings): string {\n if (ac === \"ETF\") return s.bucketAEtf;\n if (ac === \"GovtBondWL\" || ac === \"GovtBondOther\") return s.bucketABtpWl;\n return ac;\n}\n\n/**\n * Renders the \"MODELLO REDDITI PF\" declaration section (Quadro RT + Quadro RM)\n * appended to the calc output when a classification sidecar is present.\n * See docs/prd/14-dichiarazione-engine.md \"CLI Output Section\".\n */\nfunction renderDichiarazione(\n report: GainsReport,\n s: LocaleStrings,\n carryForwardWasProvided: boolean,\n): string {\n const d = report.dichiarazione!;\n const rt = d.quadroRT;\n const rm = d.quadroRM;\n const fmt = (n: number) => formatEUR(n, s.numberLocale);\n const lines: string[] = [];\n\n lines.push(SEPARATOR);\n lines.push(s.dichiarazioneHeader(d.annoImposta));\n lines.push(s.dichiarazioneNota);\n lines.push(\"\");\n\n // Quadro RT — Sezione II (redditi diversi)\n lines.push(s.quadroRTHeader);\n lines.push(` ${s.quadroRTPlusvalenze} ${fmt(rt.plusvalenze)}`);\n lines.push(` ${s.quadroRTMinusvalenze} ${fmt(rt.minusvalenze)}`);\n lines.push(` ${s.quadroRTDifferenza} ${fmt(rt.differenza)}`);\n for (const entry of rt.carryForwardApplied) {\n lines.push(\n ` ${s.quadroRTRiporto(entry.annoOrigine)} ${fmt(entry.importo)}`,\n );\n }\n let rtNLine = ` ${s.quadroRTImponibile} ${fmt(rt.imponibileNetto)}`;\n if (rt.differenza < 0) {\n rtNLine += ` (${s.quadroRTPerdita} ${fmt(Math.abs(rt.differenza))})`;\n }\n if (rt.differenza > 0 && !carryForwardWasProvided) {\n rtNLine += ` ${s.warnNoCarryForwardProvided}`;\n }\n lines.push(rtNLine);\n lines.push(` ${s.quadroRTImposta} ${fmt(rt.imposta)}`);\n if (rt.differenza < 0) {\n const riportato = rt.carryForwardRiportato.reduce(\n (sum, e) => sum + e.importo,\n 0,\n );\n lines.push(\n ` ${s.quadroRTRiportabile} ${fmt(riportato)} ${s.bucketBCarryNote}`,\n );\n }\n lines.push(\"\");\n\n // Quadro RM (redditi da capitale)\n lines.push(s.quadroRMHeader);\n if (rm.capitaleAliquota26.plusvalenze > 0) {\n lines.push(\n ` ${s.quadroRMEtf26} ${fmt(rm.capitaleAliquota26.plusvalenze)}`,\n );\n lines.push(\n ` [RM-A1] ${s.quadroRMImposta} ${fmt(rm.capitaleAliquota26.imposta)}`,\n );\n }\n if (rm.capitaleAliquota125.plusvalenze > 0) {\n lines.push(\n ` ${s.quadroRMBtp} ${fmt(rm.capitaleAliquota125.plusvalenze)}`,\n );\n lines.push(\n ` [RM-A2] ${s.quadroRMImposta} ${fmt(rm.capitaleAliquota125.imposta)}`,\n );\n }\n for (const div of rm.dividendiEsteri) {\n lines.push(\n ` ${s.quadroRMDividendi} — ${div.prodotto} (${div.isin}) ${fmt(div.lordo)}`,\n );\n lines.push(\n ` [RM-D] ${s.quadroRMRitenuta} — ${div.prodotto} (${div.isin}) ${fmt(div.rittenutaEstera)}`,\n );\n }\n for (const c of rm.cedole) {\n lines.push(\n ` ${s.quadroRMCedole} — ${c.prodotto} (${c.isin}) ${fmt(c.importo)}`,\n );\n }\n lines.push(\"\");\n\n lines.push(s.dichiarazioneWarningRow);\n lines.push(s.dichiarazioneDisclaimer);\n\n return lines.join(\"\\n\");\n}\n\nexport function renderReport(\n report: GainsReport,\n s: LocaleStrings,\n carryForwardWasProvided: boolean,\n): string {\n const fmt = (n: number) => formatEUR(n, s.numberLocale);\n const lines: string[] = [];\n\n const hasBuckets = !!(report.bucketA || report.bucketB);\n\n // Header\n lines.push(\n `${s.headerMethod}: ${report.method} | ${s.headerTaxYear}: ${report.taxYear}`,\n );\n lines.push(\"\");\n\n // Pre-table warnings (AVVISO: or WARNING:)\n for (const w of report.warnings) {\n if (w.startsWith(\"AVVISO:\") || w.startsWith(\"WARNING:\")) {\n lines.push(w);\n lines.push(\"\");\n }\n }\n\n // Column headers\n const headerCols = [\n s.headerIsin.padEnd(14),\n s.headerProduct.padEnd(20),\n s.headerQty.padStart(5),\n s.headerBuyDate.padEnd(12),\n s.headerSellDate.padEnd(12),\n s.headerBuyEur.padStart(14),\n s.headerSellEur.padStart(13),\n s.headerGainLoss.padStart(17),\n ];\n if (hasBuckets) {\n headerCols.push(s.headerBucket.padStart(8));\n }\n lines.push(headerCols.join(\" \"));\n\n // Lot rows\n for (const lot of report.lots) {\n const rowCols = [\n lot.isin.padEnd(14),\n lot.product.substring(0, 20).padEnd(20),\n String(lot.quantity).padStart(5),\n lot.buyDate.padEnd(12),\n lot.sellDate.padEnd(12),\n fmt(lot.buyCostEUR).padStart(14),\n fmt(lot.sellProceedsEUR).padStart(13),\n formatGainLoss(lot.gainLossEUR, s.numberLocale).padStart(17),\n ];\n if (hasBuckets) {\n rowCols.push((lot.bucket ?? \"\").padStart(8));\n }\n lines.push(rowCols.join(\" \"));\n }\n\n lines.push(\"\");\n lines.push(SEPARATOR);\n\n // Summary\n lines.push(`${s.summaryPlusvalenze}: ${fmt(report.plusvalenze)} EUR`);\n lines.push(`${s.summaryMinusvalenze}: ${fmt(report.minusvalenze)} EUR`);\n lines.push(`${s.summaryNetResult}: ${fmt(report.netResult)} EUR`);\n lines.push(\"\");\n lines.push(`${s.summaryWarnings}: ${report.warnings.length}`);\n lines.push(`${s.summaryGenerated}: ${report.generatedAt}`);\n lines.push(\"\");\n\n // Bucket A section\n if (report.bucketA && report.bucketA.groups.length > 0) {\n lines.push(SEPARATOR);\n lines.push(s.bucketAHeader);\n lines.push(SEPARATOR);\n for (const g of report.bucketA.groups) {\n const label = g.assetClasses\n .map((ac) => assetClassLabel(ac, s))\n .join(\", \");\n const rateDisplay = (g.taxRate * 100).toFixed(0);\n lines.push(\n `${label}: ${fmt(g.plusvalenze)} EUR → imposta: ${fmt(g.imposta)} EUR (${rateDisplay}%)`,\n );\n }\n lines.push(`${s.bucketATotalTax}: ${fmt(report.bucketA.totalImposta)} EUR`);\n lines.push(\"\");\n }\n\n // Bucket B section\n if (report.bucketB) {\n lines.push(SEPARATOR);\n lines.push(s.bucketBHeader);\n lines.push(SEPARATOR);\n lines.push(`PLUSVALENZE: ${fmt(report.bucketB.plusvalenze)} EUR`);\n lines.push(`MINUSVALENZE: ${fmt(report.bucketB.minusvalenze)} EUR`);\n if (report.bucketB.carryForwardApplied > 0) {\n lines.push(\n `${s.bucketBCarryApplied(0)}: ${fmt(report.bucketB.carryForwardApplied)} EUR`,\n );\n }\n let resultLine = `${s.bucketBResult}: ${fmt(report.bucketB.netResult)} EUR`;\n if (report.bucketB.carryForwardRemaining > 0) {\n resultLine += ` ${s.bucketBCarryNote}`;\n }\n lines.push(resultLine);\n lines.push(\"\");\n }\n\n // Mixed-buckets footnote\n if (hasBuckets) {\n lines.push(s.warnMixedBuckets);\n lines.push(\"\");\n }\n\n if (report.dichiarazione) {\n lines.push(renderDichiarazione(report, s, carryForwardWasProvided));\n } else {\n lines.push(s.disclaimer);\n }\n\n return lines.join(\"\\n\");\n}\n","import * as fs from \"node:fs\";\nimport { DEGIROParser } from \"../../parser/index.js\";\nimport { ParseError } from \"../../errors.js\";\nimport type { LocaleStrings } from \"../../i18n/types.js\";\n\nexport async function runValidate(\n positional: string[],\n flags: Record<string, string | boolean>,\n s: LocaleStrings,\n stdout: NodeJS.WritableStream,\n stderr: NodeJS.WritableStream,\n): Promise<number> {\n const filePath = positional[0];\n if (!filePath) {\n stderr.write(\"Usage: minus-tracker validate <file.csv>\\n\");\n return 2;\n }\n\n let csv: string;\n try {\n csv = fs.readFileSync(filePath, \"utf8\");\n } catch {\n stderr.write(`Cannot read file: ${filePath}\\n`);\n return 1;\n }\n\n const parser = new DEGIROParser();\n let transactions;\n try {\n transactions = parser.parse(csv);\n } catch (err) {\n if (err instanceof ParseError) {\n if (err.code === \"INVALID_CSV\") {\n stderr.write(s.errorInvalidCsv + \"\\n\");\n } else {\n stderr.write(s.errorMissingColumn(err.columnName!) + \"\\n\");\n }\n return 1;\n }\n throw err;\n }\n\n stdout.write(s.validateOk(transactions.length, 0) + \"\\n\");\n\n for (const entry of parser.warningEntries) {\n let reason: string;\n switch (entry.code) {\n case \"MISSING_ISIN\":\n reason = s.warnMissingIsin(entry.row);\n break;\n case \"UNSUPPORTED_CURRENCY\":\n reason = s.warnUnsupportedCurrency(entry.row, entry.currency);\n break;\n case \"NO_ECB_RATE\":\n reason = s.warnNoEcbRate(entry.row, entry.currency, entry.date);\n break;\n case \"QUANTITY_ZERO\":\n reason = s.warnQuantityZero(entry.row);\n break;\n case \"MISSING_ISIN_INCOME\":\n reason = s.warnMissingIsinIncome(entry.row);\n break;\n case \"ORPHAN_WITHHOLDING\":\n reason = s.warnOrphanWithholding(entry.isin, entry.date);\n break;\n }\n stdout.write(reason + \"\\n\");\n }\n\n return 0;\n}\n","import * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport * as os from \"node:os\";\nimport * as https from \"node:https\";\nimport { getActiveSnapshot, type RatesSnapshot } from \"../../rates/index.js\";\nimport type { LocaleStrings } from \"../../i18n/types.js\";\n\nexport function getSnapshotPath(): string {\n const platform = process.platform;\n let configDir: string;\n if (platform === \"win32\") {\n configDir =\n process.env.APPDATA ?? path.join(os.homedir(), \"AppData\", \"Roaming\");\n } else {\n configDir =\n process.env.XDG_CONFIG_HOME ?? path.join(os.homedir(), \".config\");\n }\n return path.join(configDir, \"minus-tracker\", \"ecb-rates.json\");\n}\n\nfunction getCoverage(snapshot: RatesSnapshot): {\n start: string;\n end: string;\n currencies: string;\n} {\n let start = \"9999-12-31\";\n let end = \"0000-01-01\";\n const currencies: string[] = [];\n\n for (const [ccy, dates] of Object.entries(snapshot)) {\n currencies.push(ccy);\n for (const d of Object.keys(dates)) {\n if (d < start) start = d;\n if (d > end) end = d;\n }\n }\n return { start, end, currencies: currencies.sort().join(\", \") };\n}\n\nexport async function fetchEcbData(\n currency: string,\n): Promise<Record<string, number>> {\n return new Promise((resolve, reject) => {\n const url = `https://data-api.ecb.europa.eu/service/data/EXR/D.${currency}.EUR.SP00.A?format=csvdata&startPeriod=2019-01-01`;\n https\n .get(url, (res) => {\n let data = \"\";\n res.on(\"data\", (chunk) => (data += chunk));\n res.on(\"end\", () => {\n const rates: Record<string, number> = {};\n const lines = data.split(\"\\n\");\n // ECB SDMX csvdata format:\n // KEY,FREQ,CURRENCY,CURRENCY_DENOM,EXR_TYPE,EXR_SUFFIX,TIME_PERIOD,OBS_VALUE,...\n // DATE is column index 6, rate is column index 7\n for (const line of lines) {\n const parts = line.split(\",\");\n if (parts.length < 8) continue;\n const date = parts[6].trim();\n const rate = parseFloat(parts[7].trim());\n if (/^\\d{4}-\\d{2}-\\d{2}$/.test(date) && !isNaN(rate) && rate > 0) {\n rates[date] = rate;\n }\n }\n resolve(rates);\n });\n res.on(\"error\", reject);\n })\n .on(\"error\", reject);\n });\n}\n\nexport async function updateRates(\n snapshotPath: string,\n stdout: NodeJS.WritableStream,\n stderr: NodeJS.WritableStream,\n s: LocaleStrings,\n): Promise<void> {\n stdout.write(s.ratesUpdateFetching + \"\\n\");\n\n let existing: RatesSnapshot = {};\n try {\n existing = JSON.parse(\n fs.readFileSync(snapshotPath, \"utf8\"),\n ) as RatesSnapshot;\n } catch {\n /* file doesn't exist yet */\n }\n\n let addedCount = 0;\n for (const currency of [\"USD\", \"GBP\", \"CHF\"]) {\n try {\n const newRates = await fetchEcbData(currency);\n const existing_ccy = existing[currency] ?? {};\n let added = 0;\n for (const [date, rate] of Object.entries(newRates)) {\n if (!existing_ccy[date]) added++;\n existing_ccy[date] = rate;\n }\n existing[currency] = existing_ccy;\n addedCount += added;\n } catch {\n stderr.write(`Failed to fetch ${currency} rates\\n`);\n }\n }\n\n fs.mkdirSync(path.dirname(snapshotPath), { recursive: true });\n fs.writeFileSync(\n snapshotPath,\n JSON.stringify(existing, null, 2) + \"\\n\",\n \"utf8\",\n );\n stdout.write(s.ratesUpdateDone(addedCount) + \"\\n\");\n stdout.write(s.ratesSnapshotWritten(snapshotPath) + \"\\n\");\n}\n\nexport async function runRates(\n positional: string[],\n flags: Record<string, string | boolean>,\n s: LocaleStrings,\n stdout: NodeJS.WritableStream,\n stderr: NodeJS.WritableStream,\n): Promise<number> {\n if (flags[\"check\"]) {\n const snapshot = getActiveSnapshot();\n const { start, end, currencies } = getCoverage(snapshot);\n stdout.write(s.ratesCoverage(start, end, currencies) + \"\\n\");\n stdout.write(s.ratesGapsNone + \"\\n\");\n return 0;\n }\n\n if (flags[\"update\"]) {\n await updateRates(getSnapshotPath(), stdout, stderr, s);\n return 0;\n }\n\n stderr.write(\"Usage: minus-tracker rates --check | --update\\n\");\n return 2;\n}\n","import { resolveLocale, saveLocale } from \"../../i18n/settings.js\";\nimport type { LocaleStrings, SupportedLocale } from \"../../i18n/types.js\";\n\nconst SUPPORTED_LOCALES: SupportedLocale[] = [\"it\", \"en\"];\n\nexport async function runConfig(\n positional: string[],\n flags: Record<string, string | boolean>,\n s: LocaleStrings,\n stdout: NodeJS.WritableStream,\n stderr: NodeJS.WritableStream,\n): Promise<number> {\n if (flags[\"lang\"] !== undefined) {\n const lang = flags[\"lang\"] as string;\n if (!SUPPORTED_LOCALES.includes(lang as SupportedLocale)) {\n stderr.write(`Unsupported locale \"${lang}\". Use: it, en\\n`);\n return 2;\n }\n saveLocale(lang as SupportedLocale);\n stdout.write(s.configLangSet(lang as SupportedLocale) + \"\\n\");\n return 0;\n }\n\n if (flags[\"show\"]) {\n const locale = resolveLocale();\n stdout.write(s.configCurrentLang(locale) + \"\\n\");\n return 0;\n }\n\n stderr.write(\"Usage: minus-tracker config --lang <it|en> | --show\\n\");\n return 2;\n}\n","/**\n * stress-test CLI command handler\n * Runs all (or a range of) stress test scenarios against the CLI binary.\n */\n\nimport * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport * as os from \"node:os\";\nimport { fileURLToPath } from \"node:url\";\nimport { spawnSync } from \"node:child_process\";\nimport { generateCsv } from \"../../stress/generator.js\";\nimport { runScenario } from \"../../stress/runner.js\";\nimport { buildReport, formatTable, formatJson } from \"../../stress/reporter.js\";\nimport type { StressManifest } from \"../../stress/generator.js\";\n\nexport async function runStressTest(\n _positionals: string[],\n flags: Record<string, string | boolean>,\n stdout: NodeJS.WritableStream,\n stderr: NodeJS.WritableStream,\n): Promise<number> {\n // 1. Parse --range flag\n const rangeStr = (flags[\"range\"] as string | undefined) ?? \"1-100\";\n const rangeParts = rangeStr.split(\"-\");\n if (rangeParts.length !== 2) {\n stderr.write(\n `Error: --range must be in the form N-M (e.g. 1-100), got: \"${rangeStr}\"\\n`,\n );\n return 2;\n }\n const rangeStart = parseInt(rangeParts[0], 10);\n const rangeEnd = parseInt(rangeParts[1], 10);\n if (\n isNaN(rangeStart) ||\n isNaN(rangeEnd) ||\n rangeStart <= 0 ||\n rangeEnd <= 0 ||\n rangeStart > rangeEnd ||\n rangeStart > 100 ||\n rangeEnd > 100\n ) {\n stderr.write(\n `Error: --range values must be positive integers between 1 and 100 with start ≤ end, got: \"${rangeStr}\"\\n`,\n );\n return 2;\n }\n\n // 2. Resolve manifest path\n const __dirname = path.dirname(fileURLToPath(import.meta.url));\n const manifestPath = path.join(__dirname, \"../data/stress-manifest.json\");\n\n // 3. Load manifest\n let manifest: StressManifest;\n try {\n const raw = fs.readFileSync(manifestPath, \"utf8\");\n manifest = JSON.parse(raw) as StressManifest;\n } catch {\n stderr.write(`Error: could not load stress manifest at ${manifestPath}\\n`);\n return 1;\n }\n\n // 4. Resolve CLI binary\n const cliBin = process.argv[1];\n\n // 5. Create output directory\n let outputDir: string;\n const outputDirFlag = flags[\"output-dir\"] as string | undefined;\n if (outputDirFlag) {\n outputDir = outputDirFlag;\n fs.mkdirSync(outputDir, { recursive: true });\n } else {\n outputDir = fs.mkdtempSync(path.join(os.tmpdir(), \"minus-tracker-stress-\"));\n }\n\n // 8. Run global checks before scenarios\n const ratesCheck = spawnSync(\"node\", [cliBin, \"rates\", \"--check\"], {\n encoding: \"utf8\",\n timeout: 15000,\n });\n if ((ratesCheck.status ?? 1) !== 0) {\n stderr.write(\"Warning: rates --check failed; ECB rates may be stale.\\n\");\n }\n\n const configShow = spawnSync(\"node\", [cliBin, \"config\", \"--show\"], {\n encoding: \"utf8\",\n timeout: 15000,\n });\n if ((configShow.status ?? 1) !== 0) {\n stderr.write(\"Warning: config --show failed.\\n\");\n }\n\n // 6. Filter scenarios by range\n const scenarios = manifest.scenarios.filter((s) => {\n const id = parseInt(s.id, 10);\n return id >= rangeStart && id <= rangeEnd;\n });\n\n // 7. Run each scenario\n const results = [];\n for (const scenario of scenarios) {\n const csv = generateCsv(scenario);\n const result = runScenario(scenario, csv, outputDir, cliBin);\n results.push(result);\n stdout.write(\n ` [${result.pass ? \"✓\" : \"✗\"}] ${scenario.id} ${scenario.slug}\\n`,\n );\n }\n\n // 9. Build and output report\n const report = buildReport(results, rangeStart, rangeEnd);\n if (flags[\"json\"]) {\n stdout.write(formatJson(report) + \"\\n\");\n } else {\n stdout.write(formatTable(report) + \"\\n\");\n }\n\n // 10. Cleanup unless --keep\n if (!flags[\"keep\"]) {\n fs.rmSync(outputDir, { recursive: true, force: true });\n }\n\n // 11. Return exit code\n return report.failed === 0 ? 0 : 1;\n}\n","/**\n * DEGIRO CSV Generator for stress testing\n * Converts manifest scenarios into DEGIRO-format CSV strings.\n */\n\n/**\n * A single transaction from the manifest\n */\nexport interface ManifestTransaction {\n date: string; // YYYY-MM-DD\n product: string;\n isin: string;\n qty: number; // positive = BUY, negative = SELL\n price: number;\n currency: string; // EUR | USD | GBP | CHF\n fee: number; // absolute EUR fee\n}\n\n/**\n * Expected outcomes for a scenario\n */\nexport interface ManifestExpect {\n calc_exit: number;\n fifo_exit: number;\n json_exit: number;\n en_exit: number;\n validate_exit: number;\n warning_count: number;\n}\n\n/**\n * A single test scenario\n */\nexport interface ManifestScenario {\n id: string;\n category: string;\n slug: string;\n description: string;\n transactions: ManifestTransaction[];\n expect: ManifestExpect;\n special?: string;\n}\n\n/**\n * The full stress test manifest\n */\nexport interface StressManifest {\n version: string;\n ecb_reference_dates: Record<string, unknown>;\n scenarios: ManifestScenario[];\n}\n\n/**\n * Convert a date from YYYY-MM-DD to DD-MM-YYYY\n */\nfunction formatDate(isoDate: string): string {\n const [yyyy, mm, dd] = isoDate.split(\"-\");\n return `${dd}-${mm}-${yyyy}`;\n}\n\n/**\n * Generate a DEGIRO-format CSV string for a given scenario.\n *\n * DEGIRO CSV format (18 columns, exact order):\n * Date,Time,Product,ISIN,Exchange,Execution centre,Quantity,Price,Local value,\n * Local value currency,Value,Value currency,Exchange rate,Transaction costs,\n * Transaction costs currency,Total,Total currency,Order ID\n *\n * Returns the CSV content as a string (header + data rows, joined by \\n)\n */\nexport function generateCsv(scenario: ManifestScenario): string {\n // Handle special cases\n if (scenario.special === \"invalid_csv\") {\n // Return garbage bytes (invalid CSV)\n return \"NOT_A_CSV\\x00\\x01\\x02\\ngarbage data\";\n }\n\n // For normal and column-removal cases, build the header first\n const allColumns = [\n \"Date\",\n \"Time\",\n \"Product\",\n \"ISIN\",\n \"Exchange\",\n \"Execution centre\",\n \"Quantity\",\n \"Price\",\n \"Local value\",\n \"Local value currency\",\n \"Value\",\n \"Value currency\",\n \"Exchange rate\",\n \"Transaction costs\",\n \"Transaction costs currency\",\n \"Total\",\n \"Total currency\",\n \"Order ID\",\n ];\n\n // Remove columns based on special field\n let columns = allColumns;\n if (scenario.special === \"missing_col_isin\") {\n columns = columns.filter((c) => c !== \"ISIN\");\n } else if (scenario.special === \"missing_col_quantity\") {\n columns = columns.filter((c) => c !== \"Quantity\");\n } else if (scenario.special === \"missing_col_price\") {\n columns = columns.filter((c) => c !== \"Price\");\n }\n\n // Helper to build a CSV row, omitting columns as needed\n const buildRow = (\n values: Record<string, string>,\n includeColumns: string[],\n ): string => {\n return includeColumns.map((col) => values[col] ?? \"\").join(\",\");\n };\n\n // Build header\n const header = columns.join(\",\");\n const rows = [header];\n\n // Build data rows\n for (let rowIndex = 0; rowIndex < scenario.transactions.length; rowIndex++) {\n const tx = scenario.transactions[rowIndex];\n\n // Calculate local value: -qty * price\n // For BUY (qty > 0): localValue is negative (money out)\n // For SELL (qty < 0): localValue is positive (money in)\n const localValue = -tx.qty * tx.price;\n const total = localValue - tx.fee;\n\n const rowValues: Record<string, string> = {\n Date: formatDate(tx.date),\n Time: \"09:00\",\n Product: tx.product,\n ISIN: tx.isin,\n Exchange: \"XEUR\",\n \"Execution centre\": \"XEUR\",\n Quantity: String(tx.qty),\n Price: tx.price.toFixed(2),\n \"Local value\": localValue.toFixed(2),\n \"Local value currency\": tx.currency,\n Value: localValue.toFixed(2),\n \"Value currency\": \"EUR\",\n \"Exchange rate\": \"1\",\n \"Transaction costs\": (-Math.abs(tx.fee)).toFixed(2),\n \"Transaction costs currency\": \"EUR\",\n Total: total.toFixed(2),\n \"Total currency\": \"EUR\",\n \"Order ID\": `stress-${scenario.id}-${rowIndex}`,\n };\n\n const row = buildRow(rowValues, columns);\n rows.push(row);\n }\n\n return rows.join(\"\\n\");\n}\n","/**\n * CLI runner for minus-tracker stress test scenarios\n * Executes CLI commands against generated CSV files and collects results.\n */\n\nimport { spawnSync } from \"node:child_process\";\nimport { writeFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport type { ManifestScenario } from \"./generator.js\";\n\n/**\n * Result of running a single CLI command\n */\nexport interface CommandResult {\n cmd: string;\n exitCode: number;\n stdout: string;\n stderr: string;\n pass: boolean;\n failure?: string;\n}\n\n/**\n * Result of running all commands for a scenario\n */\nexport interface ScenarioResult {\n id: string;\n category: string;\n slug: string;\n description: string;\n csvFile: string;\n results: CommandResult[];\n pass: boolean;\n warningCheckPass: boolean;\n expectedWarningCount: number;\n actualWarningCount: number;\n}\n\n/**\n * Extract the warning count from calc output.\n * The English renderer emits \"WARNINGS: N\" and the Italian renderer emits \"AVVERTENZE: N\".\n * We parse that numeric value directly rather than counting matching lines.\n */\nfunction countWarnings(text: string): number {\n if (!text) return 0;\n const match = text.match(/(?:warnings|avvertenze)\\s*:\\s*(\\d+)/i);\n return match ? parseInt(match[1], 10) : 0;\n}\n\n/**\n * Check if a string is valid JSON with required keys\n */\nfunction isValidJson(jsonStr: string): boolean {\n try {\n const obj = JSON.parse(jsonStr);\n return (\n typeof obj === \"object\" &&\n obj !== null &&\n \"plusvalenze\" in obj &&\n \"minusvalenze\" in obj &&\n \"lots\" in obj\n );\n } catch {\n return false;\n }\n}\n\n/**\n * Run a single CLI command and return its result\n */\nfunction runCommand(\n cliBin: string,\n args: string[],\n cmdLabel: string,\n expectedExitCode: number,\n outputShapeCheck: (\n stdout: string,\n stderr: string,\n exitCode: number,\n ) => { valid: boolean; reason?: string },\n): CommandResult {\n // Determine how to invoke the CLI\n const isJsFile = cliBin.endsWith(\".js\");\n let result;\n\n if (isJsFile) {\n // Run: node <cliBin> <args>\n result = spawnSync(\"node\", [cliBin, ...args], {\n encoding: \"utf8\",\n timeout: 15000,\n env: { ...process.env, MINUS_TRACKER_LANG: undefined },\n });\n } else {\n // Run: <cliBin> <args> (directly executable)\n result = spawnSync(cliBin, args, {\n encoding: \"utf8\",\n timeout: 15000,\n env: { ...process.env, MINUS_TRACKER_LANG: undefined },\n });\n }\n\n const exitCode = result.status ?? 1; // null status (timeout/error) → treat as 1\n const stdout = result.stdout || \"\";\n const stderr = result.stderr || \"\";\n\n // Check exit code match\n const exitCodeMatch = exitCode === expectedExitCode;\n\n // Check output shape\n const shapeCheck = outputShapeCheck(stdout, stderr, exitCode);\n\n let pass = exitCodeMatch && shapeCheck.valid;\n let failure: string | undefined;\n\n if (!exitCodeMatch) {\n failure = `exit code mismatch: expected ${expectedExitCode}, got ${exitCode}`;\n } else if (!shapeCheck.valid) {\n failure = shapeCheck.reason;\n }\n\n return {\n cmd: cmdLabel,\n exitCode,\n stdout,\n stderr,\n pass,\n failure,\n };\n}\n\n/**\n * Run all CLI commands for a scenario and return the result\n */\nexport function runScenario(\n scenario: ManifestScenario,\n csvContent: string,\n outputDir: string,\n cliBin: string,\n): ScenarioResult {\n // Write CSV to temp file\n const csvFileName = `${scenario.id}-${scenario.slug}.csv`;\n const csvFile = join(outputDir, csvFileName);\n writeFileSync(csvFile, csvContent, \"utf8\");\n\n const results: CommandResult[] = [];\n\n // Run calc command (LIFO)\n results.push(\n runCommand(\n cliBin,\n [\"calc\", csvFile],\n \"calc\",\n scenario.expect.calc_exit,\n (stdout) => {\n if (scenario.expect.calc_exit === 0) {\n const hasPlus = /plusvalenze|plusvalues/i.test(stdout);\n return hasPlus\n ? { valid: true }\n : {\n valid: false,\n reason: \"calc output missing plusvalenze/plusvalues (exit 0)\",\n };\n }\n return { valid: true };\n },\n ),\n );\n\n // Run calc --method FIFO\n results.push(\n runCommand(\n cliBin,\n [\"calc\", \"--method\", \"FIFO\", csvFile],\n \"calc --method FIFO\",\n scenario.expect.fifo_exit,\n (stdout) => {\n if (scenario.expect.fifo_exit === 0) {\n const hasPlus = /plusvalenze|plusvalues/i.test(stdout);\n return hasPlus\n ? { valid: true }\n : {\n valid: false,\n reason:\n \"calc --method FIFO output missing plusvalenze/plusvalues (exit 0)\",\n };\n }\n return { valid: true };\n },\n ),\n );\n\n // Run calc --json\n results.push(\n runCommand(\n cliBin,\n [\"calc\", \"--json\", csvFile],\n \"calc --json\",\n scenario.expect.json_exit,\n (stdout) => {\n if (scenario.expect.json_exit === 0) {\n const isValid = isValidJson(stdout);\n return isValid\n ? { valid: true }\n : {\n valid: false,\n reason:\n \"calc --json output is not valid JSON with required keys (exit 0)\",\n };\n }\n return { valid: true };\n },\n ),\n );\n\n // Run calc --lang en\n results.push(\n runCommand(\n cliBin,\n [\"calc\", \"--lang\", \"en\", csvFile],\n \"calc --lang en\",\n scenario.expect.en_exit,\n (stdout) => {\n if (scenario.expect.en_exit === 0) {\n const nonEmpty = stdout.trim().length > 0;\n return nonEmpty\n ? { valid: true }\n : {\n valid: false,\n reason: \"calc --lang en output is empty (exit 0)\",\n };\n }\n return { valid: true };\n },\n ),\n );\n\n // Run validate\n results.push(\n runCommand(\n cliBin,\n [\"validate\", csvFile],\n \"validate\",\n scenario.expect.validate_exit,\n (stdout) => {\n if (scenario.expect.validate_exit !== 0) return { valid: true };\n const nonEmpty = stdout.trim().length > 0;\n return nonEmpty\n ? { valid: true }\n : {\n valid: false,\n reason: \"validate output is empty\",\n };\n },\n ),\n );\n\n // Count warnings using English locale calc output (immune to locale config)\n // English output emits \"WARNINGS: N\" which matches the /warn/i pattern reliably\n const enCalcResult = results[3]; // calc --lang en is always index 3\n const actualWarningCount = countWarnings(enCalcResult.stdout);\n const warningCheckPass = actualWarningCount >= scenario.expect.warning_count;\n\n // Determine overall pass\n const pass = results.every((r) => r.pass) && warningCheckPass;\n\n return {\n id: scenario.id,\n category: scenario.category,\n slug: scenario.slug,\n description: scenario.description,\n csvFile,\n results,\n pass,\n warningCheckPass,\n expectedWarningCount: scenario.expect.warning_count,\n actualWarningCount,\n };\n}\n","/**\n * Reporter for minus-tracker stress test suite.\n * Formats stress test results into human-readable table + summary.\n */\n\nimport type { ScenarioResult } from \"./runner.js\";\n\n/**\n * Aggregated stress test report\n */\nexport interface StressReport {\n totalScenarios: number;\n passed: number;\n failed: number;\n results: ScenarioResult[];\n rangeStart: number; // 1-based\n rangeEnd: number;\n}\n\n/**\n * Aggregates a list of ScenarioResult into a StressReport\n */\nexport function buildReport(\n results: ScenarioResult[],\n rangeStart: number,\n rangeEnd: number,\n): StressReport {\n const passed = results.filter((r) => r.pass).length;\n const failed = results.length - passed;\n\n return {\n totalScenarios: results.length,\n passed,\n failed,\n results,\n rangeStart,\n rangeEnd,\n };\n}\n\n/**\n * Format a ScenarioResult as a JSON string\n */\nexport function formatJson(report: StressReport): string {\n return JSON.stringify(report, null, 2);\n}\n\n/**\n * Format a StressReport as a human-readable ASCII table\n */\nexport function formatTable(report: StressReport): string {\n const lines: string[] = [];\n\n // Header\n lines.push(\"STRESS TEST — minus-tracker\");\n lines.push(\n `Scenari: ${report.totalScenarios} | Passati: ${report.passed} | Falliti: ${report.failed}`,\n );\n lines.push(\"\");\n\n // Column header\n const idCol = \"ID\";\n const categoryCol = \"CATEGORIA\";\n const descCol = \"DESCRIZIONE\";\n const resultCol = \"ESITO\";\n\n lines.push(formatRow(idCol, categoryCol, descCol, resultCol));\n\n // Rows for each scenario\n for (const result of report.results) {\n const idStr = result.id.padStart(3, \"0\");\n const status = result.pass ? \"✓ PASS\" : \"✗ FAIL\";\n\n lines.push(formatRow(idStr, result.category, result.description, status));\n\n // If failed, add failure details\n if (!result.pass) {\n for (const cmdResult of result.results) {\n if (!cmdResult.pass) {\n if (cmdResult.failure) {\n let failureText = \"\";\n\n // Check for exit code mismatch pattern\n if (cmdResult.failure.includes(\"exit code mismatch\")) {\n const match = cmdResult.failure.match(\n /expected (\\d+), got (\\d+)/,\n );\n if (match) {\n failureText = `atteso exit ${match[1]}, ottenuto exit ${match[2]}`;\n }\n } else {\n // Output shape check failed\n failureText = `output shape non valido — ${cmdResult.failure}`;\n }\n\n lines.push(` → ${cmdResult.cmd}: ${failureText}`);\n }\n }\n }\n\n // Check warning count\n if (!result.warningCheckPass) {\n lines.push(\n ` → avvertenze: attese ≥${result.expectedWarningCount}, trovate ${result.actualWarningCount}`,\n );\n }\n }\n }\n\n // Summary footer\n lines.push(\"\");\n lines.push(`PASSATI: ${report.passed}`);\n lines.push(`FALLITI: ${report.failed}`);\n lines.push(\"\");\n\n return lines.join(\"\\n\");\n}\n\n/**\n * Format a single table row with fixed column widths\n */\nfunction formatRow(\n id: string,\n category: string,\n description: string,\n esito: string,\n): string {\n // Column widths: ID=4, CATEGORIA=22, DESCRIZIONE=40, ESITO=8\n const idWidth = 4;\n const categoryWidth = 22;\n const descWidth = 40;\n const esitoWidth = 8;\n\n // Pad/truncate columns\n const idCol = id.padEnd(idWidth);\n const categoryCol = truncateOrPad(category, categoryWidth);\n const descCol = truncateOrPad(description, descWidth);\n const esitoCol = esito.padEnd(esitoWidth);\n\n return `${idCol} ${categoryCol} ${descCol} ${esitoCol}`;\n}\n\n/**\n * Truncate a string to max width with \"..\" suffix, or pad to width\n */\nfunction truncateOrPad(str: string, maxWidth: number): string {\n if (str.length > maxWidth) {\n return str.slice(0, maxWidth - 2) + \"..\";\n }\n return str.padEnd(maxWidth);\n}\n","import * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport { DEGIROParser } from \"../../parser/index.js\";\nimport { Classifier } from \"../../classifier/index.js\";\nimport { ParseError } from \"../../errors.js\";\nimport type { LocaleStrings } from \"../../i18n/types.js\";\nimport type { ClassificationMap } from \"../../types.js\";\n\nexport async function runClassify(\n positional: string[],\n flags: Record<string, string | boolean>,\n s: LocaleStrings,\n stdout: NodeJS.WritableStream,\n stderr: NodeJS.WritableStream,\n): Promise<number> {\n const offline = Boolean(flags[\"offline\"]);\n\n // TTY check — FIRST, before any file I/O\n if (!process.stdin.isTTY && !offline) {\n stderr.write(s.classifyNonTtyError + \"\\n\");\n return 2;\n }\n\n const csvPath = positional[0];\n if (!csvPath) {\n stderr.write(\"Usage: minus-tracker classify [--offline] <file.csv>\\n\");\n return 2;\n }\n\n let csv: string;\n try {\n csv = fs.readFileSync(csvPath, \"utf8\");\n } catch {\n stderr.write(`Cannot read file: ${csvPath}\\n`);\n return 1;\n }\n\n const parser = new DEGIROParser();\n let transactions;\n try {\n transactions = parser.parse(csv);\n } catch (err) {\n if (err instanceof ParseError) {\n if (err.code === \"INVALID_CSV\") {\n stderr.write(s.errorInvalidCsv + \"\\n\");\n } else {\n stderr.write(s.errorMissingColumn(err.columnName!) + \"\\n\");\n }\n return 1;\n }\n throw err;\n }\n\n // Derive sidecar path: replace .csv extension with .classify.json\n const base = csvPath.replace(/\\.csv$/i, \"\");\n const sidecarPath = base + \".classify.json\";\n\n if (offline) {\n // Offline mode: skip OpenFIGI; write stub user-confirmed entries for each unique ISIN\n stdout.write(s.classifyOfflineWarning + \"\\n\");\n\n const isinToProduct = new Map<string, string>();\n for (const tx of transactions) {\n if (tx.isin && !isinToProduct.has(tx.isin)) {\n isinToProduct.set(tx.isin, tx.product);\n }\n }\n\n const classifications: ClassificationMap = {};\n for (const [isin, product] of isinToProduct.entries()) {\n classifications[isin] = {\n product,\n assetClass: \"ETF\",\n bucketGain: \"A\",\n bucketLoss: \"B\",\n taxRate: 0.26,\n whiteListed: null,\n confirmedByUser: true,\n source: \"user\",\n };\n }\n\n const sidecarContent = JSON.stringify(\n {\n version: 1,\n generatedAt: new Date().toISOString(),\n classifications,\n warnings: [],\n },\n null,\n 2,\n );\n\n try {\n fs.writeFileSync(sidecarPath, sidecarContent, \"utf-8\");\n } catch {\n stderr.write(`Cannot write sidecar: ${sidecarPath}\\n`);\n return 1;\n }\n\n stdout.write(s.classifyWritten(sidecarPath) + \"\\n\");\n return 0;\n }\n\n // Online interactive mode\n const classifier = new Classifier({ interactive: true });\n\n if (fs.existsSync(sidecarPath)) {\n const existingMap = await classifier.load(sidecarPath);\n const confirmedCount = Object.values(existingMap).filter(\n (e) => e.confirmedByUser,\n ).length;\n stdout.write(s.classifyMergePrompt(confirmedCount));\n\n const answer = await readLine(process.stdin);\n if (answer.trim().toLowerCase() === \"n\") {\n return 0;\n }\n }\n\n await classifier.classify(transactions, sidecarPath);\n stdout.write(s.classifyWritten(path.resolve(sidecarPath)) + \"\\n\");\n return 0;\n}\n\nfunction readLine(stream: NodeJS.ReadableStream): Promise<string> {\n return new Promise((resolve) => {\n let buf = \"\";\n const onData = (chunk: Buffer | string) => {\n buf += chunk.toString();\n const nl = buf.indexOf(\"\\n\");\n if (nl !== -1) {\n stream.removeListener(\"data\", onData);\n resolve(buf.slice(0, nl));\n }\n };\n stream.on(\"data\", onData);\n });\n}\n"],"mappings":";;;AAAA,SAAS,iBAAiB;;;ACEnB,IAAM,KAAoB;AAAA,EAC/B,cAAc;AAAA,EAEd,iBAAiB;AAAA,EACjB,oBAAoB,CAAC,QAAQ,kCAAkC,GAAG;AAAA,EAClE,iBAAiB,CAAC,MAAM,SACtB,gCAAgC,IAAI,YAAY,IAAI;AAAA,EAEtD,iBAAiB,CAAC,QAAQ,QAAQ,GAAG;AAAA,EACrC,yBAAyB,CAAC,KAAK,aAC7B,QAAQ,GAAG,2BAA2B,QAAQ;AAAA,EAChD,eAAe,CAAC,KAAK,UAAU,SAC7B,QAAQ,GAAG,0BAA0B,QAAQ,YAAY,IAAI;AAAA,EAC/D,kBAAkB,CAAC,QAAQ,QAAQ,GAAG;AAAA,EACtC,uBAAuB,CAAC,QACtB,QAAQ,GAAG;AAAA,EACb,uBAAuB,CAAC,MAAM,SAC5B,uBAAuB,IAAI,IAAI,IAAI;AAAA,EAErC,mBACE;AAAA,EAEF,cAAc;AAAA,EACd,eAAe;AAAA,EACf,YAAY;AAAA,EACZ,eAAe;AAAA,EACf,WAAW;AAAA,EACX,eAAe;AAAA,EACf,gBAAgB;AAAA,EAChB,cAAc;AAAA,EACd,eAAe;AAAA,EACf,gBAAgB;AAAA,EAEhB,oBAAoB;AAAA,EACpB,qBAAqB;AAAA,EACrB,kBAAkB;AAAA,EAClB,iBAAiB;AAAA,EACjB,kBAAkB;AAAA,EAElB,YAAY,CAAC,OAAO,WAClB,OAAO,KAAK,4BAA4B,MAAM;AAAA,EAChD,cAAc,CAAC,OAAO,WACpB,WAAW,KAAK,oBAAoB,MAAM;AAAA,EAE5C,eAAe,CAAC,OAAO,KAAK,eAC1B,cAAc,KAAK,WAAM,GAAG,cAAc,UAAU;AAAA,EACtD,eAAe;AAAA,EACf,WAAW,CAAC,SAAS,WAAW,IAAI;AAAA,EACpC,qBAAqB;AAAA,EACrB,iBAAiB,CAAC,MAAM,wBAAwB,CAAC;AAAA,EACjD,sBAAsB,CAACA,UAAS,uBAAuBA,KAAI;AAAA,EAC3D,sBACE;AAAA,EACF,uBACE;AAAA,EAEF,eAAe,CAAC,SAAS,wBAAwB,IAAI;AAAA,EACrD,mBAAmB,CAAC,SAAS,oBAAoB,IAAI;AAAA,EAErD,kBAAkB,CAAC,MAAM,qCAAqC,CAAC;AAAA,EAC/D,kBAAkB,CAAC,MAAM,SAAS,QAAQ,SACxC,aAAa,OAAO,KAAK,IAAI,YAAO,MAAM,MAAM,IAAI;AAAA,EACtD,iBAAiB;AAAA,EACjB,wBAAwB;AAAA,EACxB,cAAc,CAAC,WAAW,UACxB,+BAA+B,SAAS,IAAI,KAAK;AAAA,EACnD,iBAAiB,CAACA,UAAS,YAAYA,KAAI;AAAA,EAC3C,qBAAqB,CAAC,aACpB,0BAAuB,QAAQ;AAAA,EACjC,wBAAwB;AAAA,EACxB,qBAAqB,CAAC,SACpB,0BAA0B,IAAI;AAAA,EAChC,qBACE;AAAA,EAEF,eAAe;AAAA,EACf,eAAe;AAAA,EACf,YAAY;AAAA,EACZ,cAAc;AAAA,EACd,iBAAiB;AAAA,EACjB,qBAAqB,CAAC,SAAS,WAAW,IAAI;AAAA,EAC9C,eAAe;AAAA,EACf,kBAAkB;AAAA,EAClB,kBACE;AAAA,EACF,iBACE;AAAA,EAGF,cAAc;AAAA,EACd,sBAAsB,CAAC,SACrB,QAAQ,IAAI;AAAA,EACd,2BACE;AAAA,EAEF,YAAY;AAAA;AAAA,EAGZ,qBAAqB,CAAC,SACpB,oDAA0C,IAAI;AAAA,EAChD,mBACE;AAAA,EACF,yBACE;AAAA,EACF,gBAAgB;AAAA,EAChB,qBAAqB;AAAA,EACrB,sBAAsB;AAAA,EACtB,oBAAoB;AAAA,EACpB,iBAAiB,CAAC,SAAS,mBAAmB,IAAI;AAAA,EAClD,oBAAoB;AAAA,EACpB,iBAAiB;AAAA,EACjB,qBAAqB;AAAA,EACrB,iBAAiB;AAAA,EACjB,gBAAgB;AAAA,EAChB,eAAe;AAAA,EACf,iBAAiB;AAAA,EACjB,aAAa;AAAA,EACb,mBAAmB;AAAA,EACnB,kBAAkB;AAAA,EAClB,gBAAgB;AAAA,EAChB,yBACE;AAAA,EACF,4BAA4B;AAAA,EAC5B,4BAA4B;AAC9B;;;AC5HO,IAAM,KAAoB;AAAA,EAC/B,cAAc;AAAA,EAEd,iBAAiB;AAAA,EACjB,oBAAoB,CAAC,QAAQ,4BAA4B,GAAG;AAAA,EAC5D,iBAAiB,CAAC,MAAM,SAAS,yBAAyB,IAAI,OAAO,IAAI;AAAA,EAEzE,iBAAiB,CAAC,QAAQ,OAAO,GAAG;AAAA,EACpC,yBAAyB,CAAC,KAAK,aAC7B,OAAO,GAAG,0BAA0B,QAAQ;AAAA,EAC9C,eAAe,CAAC,KAAK,UAAU,SAC7B,OAAO,GAAG,qBAAqB,QAAQ,OAAO,IAAI;AAAA,EACpD,kBAAkB,CAAC,QAAQ,OAAO,GAAG;AAAA,EACrC,uBAAuB,CAAC,QACtB,OAAO,GAAG;AAAA,EACZ,uBAAuB,CAAC,MAAM,SAC5B,sBAAsB,IAAI,IAAI,IAAI;AAAA,EAEpC,mBACE;AAAA,EAEF,cAAc;AAAA,EACd,eAAe;AAAA,EACf,YAAY;AAAA,EACZ,eAAe;AAAA,EACf,WAAW;AAAA,EACX,eAAe;AAAA,EACf,gBAAgB;AAAA,EAChB,cAAc;AAAA,EACd,eAAe;AAAA,EACf,gBAAgB;AAAA,EAEhB,oBAAoB;AAAA,EACpB,qBAAqB;AAAA,EACrB,kBAAkB;AAAA,EAClB,iBAAiB;AAAA,EACjB,kBAAkB;AAAA,EAElB,YAAY,CAAC,OAAO,WAClB,OAAO,KAAK,yBAAyB,MAAM;AAAA,EAC7C,cAAc,CAAC,OAAO,WAAW,SAAS,KAAK,kBAAkB,MAAM;AAAA,EAEvE,eAAe,CAAC,OAAO,KAAK,eAC1B,aAAa,KAAK,WAAM,GAAG,kBAAkB,UAAU;AAAA,EACzD,eAAe;AAAA,EACf,WAAW,CAAC,SAAS,SAAS,IAAI;AAAA,EAClC,qBAAqB;AAAA,EACrB,iBAAiB,CAAC,MAAM,eAAe,CAAC;AAAA,EACxC,sBAAsB,CAACC,UAAS,uBAAuBA,KAAI;AAAA,EAC3D,sBAAsB;AAAA,EACtB,uBAAuB;AAAA,EAEvB,eAAe,CAAC,SAAS,oBAAoB,IAAI;AAAA,EACjD,mBAAmB,CAAC,SAAS,qBAAqB,IAAI;AAAA,EAEtD,kBAAkB,CAAC,MAAM,yBAAyB,CAAC;AAAA,EACnD,kBAAkB,CAAC,MAAM,SAAS,QAAQ,SACxC,aAAa,OAAO,KAAK,IAAI,YAAO,MAAM,MAAM,IAAI;AAAA,EACtD,iBAAiB;AAAA,EACjB,wBAAwB;AAAA,EACxB,cAAc,CAAC,WAAW,UACxB,4BAA4B,SAAS,IAAI,KAAK;AAAA,EAChD,iBAAiB,CAACA,UAAS,YAAYA,KAAI;AAAA,EAC3C,qBAAqB,CAAC,aACpB,wBAAwB,QAAQ;AAAA,EAClC,wBAAwB;AAAA,EACxB,qBAAqB,CAAC,SACpB,sBAAsB,IAAI;AAAA,EAC5B,qBACE;AAAA,EAEF,eAAe;AAAA,EACf,eAAe;AAAA,EACf,YAAY;AAAA,EACZ,cAAc;AAAA,EACd,iBAAiB;AAAA,EACjB,qBAAqB,CAAC,SAAS,SAAS,IAAI;AAAA,EAC5C,eAAe;AAAA,EACf,kBAAkB;AAAA,EAClB,kBAAkB;AAAA,EAClB,iBACE;AAAA,EAGF,cAAc;AAAA,EACd,sBAAsB,CAAC,SACrB,QAAQ,IAAI;AAAA,EACd,2BACE;AAAA,EAEF,YAAY;AAAA;AAAA,EAGZ,qBAAqB,CAAC,SAAS,8CAAoC,IAAI;AAAA,EACvE,mBACE;AAAA,EACF,yBACE;AAAA,EACF,gBAAgB;AAAA,EAChB,qBAAqB;AAAA,EACrB,sBAAsB;AAAA,EACtB,oBAAoB;AAAA,EACpB,iBAAiB,CAAC,SAAS,sBAAsB,IAAI;AAAA,EACrD,oBAAoB;AAAA,EACpB,iBAAiB;AAAA,EACjB,qBAAqB;AAAA,EACrB,iBAAiB;AAAA,EACjB,gBAAgB;AAAA,EAChB,eAAe;AAAA,EACf,iBAAiB;AAAA,EACjB,aAAa;AAAA,EACb,mBAAmB;AAAA,EACnB,kBAAkB;AAAA,EAClB,gBAAgB;AAAA,EAChB,yBACE;AAAA,EACF,4BAA4B;AAAA,EAC5B,4BAA4B;AAC9B;;;ACxHA,YAAY,QAAQ;AACpB,YAAY,QAAQ;AACpB,YAAY,UAAU;AAGtB,IAAM,oBAAuC,CAAC,MAAM,IAAI;AAEjD,SAAS,gBAAwB;AACtC,MAAI,QAAQ,aAAa,SAAS;AAChC,UAAM,UACJ,QAAQ,IAAI,WAAgB,UAAQ,WAAQ,GAAG,WAAW,SAAS;AACrE,WAAY,UAAK,SAAS,iBAAiB,aAAa;AAAA,EAC1D;AACA,QAAM,MAAM,QAAQ,IAAI,mBAAwB,UAAQ,WAAQ,GAAG,SAAS;AAC5E,SAAY,UAAK,KAAK,iBAAiB,aAAa;AACtD;AAEO,SAAS,aAAiC;AAC/C,MAAI;AACF,UAAM,MAAS,gBAAa,cAAc,GAAG,MAAM;AACnD,WAAO,KAAK,MAAM,GAAG;AAAA,EACvB,QAAQ;AACN,WAAO,CAAC;AAAA,EACV;AACF;AAEO,SAAS,WAAW,MAA6B;AACtD,QAAM,aAAa,cAAc;AACjC,EAAG,aAAe,aAAQ,UAAU,GAAG,EAAE,WAAW,KAAK,CAAC;AAC1D,QAAM,WAAW,WAAW;AAC5B,QAAM,UAA8B,EAAE,GAAG,UAAU,QAAQ,KAAK;AAChE,EAAG,iBAAc,YAAY,KAAK,UAAU,SAAS,MAAM,CAAC,IAAI,MAAM,MAAM;AAC9E;AAEO,SAAS,cAAc,SAAmC;AAE/D,MAAI,YAAY,QAAW;AACzB,QAAI,CAAC,kBAAkB,SAAS,OAA0B,GAAG;AAC3D,cAAQ,OAAO,MAAM,uBAAuB,OAAO;AAAA,CAAkB;AACrE,cAAQ,KAAK,CAAC;AAAA,IAChB;AACA,WAAO;AAAA,EACT;AAEA,QAAM,UAAU,QAAQ,IAAI;AAC5B,MAAI,YAAY,QAAW;AACzB,QAAI,CAAC,kBAAkB,SAAS,OAA0B,GAAG;AAC3D,cAAQ,OAAO,MAAM,uBAAuB,OAAO;AAAA,CAAkB;AACrE,cAAQ,KAAK,CAAC;AAAA,IAChB;AACA,WAAO;AAAA,EACT;AAEA,QAAM,SAAS,WAAW;AAC1B,MAAI,OAAO,UAAU,kBAAkB,SAAS,OAAO,MAAM,GAAG;AAC9D,WAAO,OAAO;AAAA,EAChB;AAEA,SAAO;AACT;;;AC7CO,SAAS,WAAW,QAAwC;AACjE,SAAO,WAAW,OAAO,KAAK;AAChC;;;AChBO,IAAM,aAAN,cAAyB,MAAM;AAAA,EAC3B;AAAA,EACA;AAAA,EAIT,YAAY,MAA0B,YAAqB;AACzD,UAAM,MACJ,SAAS,gBACL,iCACA,4BAA4B,UAAU;AAC5C,UAAM,GAAG;AACT,SAAK,OAAO;AACZ,SAAK,OAAO;AACZ,SAAK,aAAa;AAAA,EACpB;AACF;AAEO,IAAM,mBAAN,cAA+B,MAAM;AAAA,EACjC;AAAA,EACA;AAAA,EAET,YAAY,MAAc,MAAc;AACtC,UAAM,yBAAyB,IAAI,OAAO,IAAI,EAAE;AAChD,SAAK,OAAO;AACZ,SAAK,OAAO;AACZ,SAAK,OAAO;AAAA,EACd;AACF;AAEO,IAAM,sBAAN,cAAkC,MAAM;AAAA,EACpC;AAAA,EAOT,YAAY,MAAmC,SAAkB;AAC/D;AAAA,MACE,YACG,SAAS,sBACN,2BACA,SAAS,oBACP,6BACA,SAAS,sBACP,mCACA,SAAS,kBACP,sCACA;AAAA,IACd;AACA,SAAK,OAAO;AACZ,SAAK,OAAO;AAAA,EACd;AACF;;;ACtDA,YAAYC,SAAQ;AACpB,YAAYC,WAAU;AACtB,YAAYC,SAAQ;;;ACFpB,SAAS,qBAAqB;AAC9B,YAAYC,WAAU;AACtB,YAAYC,SAAQ;AAKpB,IAAI,WAA6C;AAEjD,SAAS,qBAA2C;AAClD,MAAI,aAAa,OAAW,QAAO;AACnC,QAAM,YAAiB,cAAQ,cAAc,YAAY,GAAG,CAAC;AAC7D,QAAM,cAAmB,WAAK,WAAW,wBAAwB;AACjE,MAAI;AACF,eAAW,KAAK;AAAA,MACX,iBAAa,aAAa,MAAM;AAAA,IACrC;AAAA,EACF,QAAQ;AACN,eAAW;AAAA,EACb;AACA,SAAO;AACT;AAEA,SAAS,kBAAwC;AAC/C,QAAM,WAAW,QAAQ;AACzB,MAAI;AACJ,MAAI,aAAa,SAAS;AACxB,gBACE,QAAQ,IAAI,WACP,WAAK,QAAQ,IAAI,eAAe,KAAK,WAAW,SAAS;AAAA,EAClE,OAAO;AACL,gBACE,QAAQ,IAAI,mBACP,WAAK,QAAQ,IAAI,QAAQ,KAAK,SAAS;AAAA,EAChD;AACA,QAAM,WAAgB,WAAK,WAAW,iBAAiB,gBAAgB;AACvE,MAAI;AACF,WAAO,KAAK,MAAS,iBAAa,UAAU,MAAM,CAAC;AAAA,EACrD,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEO,SAAS,oBAAmC;AACjD,QAAM,UAAU,mBAAmB;AACnC,QAAM,OAAO,gBAAgB;AAC7B,MAAI,CAAC,WAAW,CAAC,MAAM;AACrB,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EACF;AACA,MAAI,CAAC,QAAS,QAAO;AACrB,MAAI,CAAC,KAAM,QAAO;AAElB,QAAM,SAAwB,CAAC;AAC/B,aAAW,OAAO,oBAAI,IAAI,CAAC,GAAG,OAAO,KAAK,OAAO,GAAG,GAAG,OAAO,KAAK,IAAI,CAAC,CAAC,GAAG;AAC1E,WAAO,GAAG,IAAI,EAAE,GAAI,QAAQ,GAAG,KAAK,CAAC,GAAI,GAAI,KAAK,GAAG,KAAK,CAAC,EAAG;AAAA,EAChE;AACA,SAAO;AACT;AAoBA,SAAS,aAAa,SAAiB,MAAsB;AAC3D,QAAM,IAAI,oBAAI,KAAK,UAAU,YAAY;AACzC,IAAE,WAAW,EAAE,WAAW,IAAI,IAAI;AAClC,SAAO,EAAE,YAAY,EAAE,MAAM,GAAG,EAAE;AACpC;AAWO,SAAS,WACd,UACA,MACA,UACe;AACf,MAAI,aAAa,MAAO,QAAO;AAE/B,QAAM,IAAI,YAAY,kBAAkB;AACxC,QAAM,gBAAgB,EAAE,QAAQ;AAChC,MAAI,CAAC,cAAe,QAAO;AAE3B,WAAS,IAAI,GAAG,KAAK,GAAG,KAAK;AAC3B,UAAM,IAAI,MAAM,IAAI,OAAO,aAAa,MAAM,CAAC;AAC/C,QAAI,cAAc,CAAC,MAAM,QAAW;AAClC,aAAO,cAAc,CAAC;AAAA,IACxB;AAAA,EACF;AACA,SAAO;AACT;;;ACxGO,SAAS,iBAAiB,GAAyB;AACxD,UAAQ,EAAE,MAAM;AAAA,IACd,KAAK;AACH,aAAO,OAAO,EAAE,GAAG;AAAA,IACrB,KAAK;AACH,aAAO,OAAO,EAAE,GAAG,0BAA0B,EAAE,QAAQ;AAAA,IACzD,KAAK;AACH,aAAO,OAAO,EAAE,GAAG,qBAAqB,EAAE,QAAQ,OAAO,EAAE,IAAI;AAAA,IACjE,KAAK;AACH,aAAO,OAAO,EAAE,GAAG;AAAA,IACrB,KAAK;AACH,aAAO,OAAO,EAAE,GAAG;AAAA,IACrB,KAAK;AACH,aAAO,uBAAuB,EAAE,IAAI,OAAO,EAAE,IAAI;AAAA,EACrD;AACF;;;ACdA,IAAM,mBAAmB;AAAA,EACvB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAIA,IAAM,kBAAkB,CAAC,YAAY,UAAU,YAAY,QAAQ;AACnE,IAAM,eAAe,CAAC,gBAAgB,eAAe,UAAU;AAuB/D,SAAS,YAAY,MAAwB;AAC3C,QAAM,SAAmB,CAAC;AAC1B,MAAI,IAAI;AAER,SAAO,KAAK,KAAK,QAAQ;AAEvB,QAAI,MAAM,KAAK,OAAQ;AAEvB,QAAI,KAAK,CAAC,MAAM,KAAK;AAEnB,UAAI,QAAQ;AACZ;AACA,aAAO,IAAI,KAAK,QAAQ;AACtB,YAAI,KAAK,CAAC,MAAM,OAAO,IAAI,IAAI,KAAK,UAAU,KAAK,IAAI,CAAC,MAAM,KAAK;AAEjE,mBAAS;AACT,eAAK;AAAA,QACP,WAAW,KAAK,CAAC,MAAM,KAAK;AAC1B;AACA;AAAA,QACF,OAAO;AACL,mBAAS,KAAK,GAAG;AAAA,QACnB;AAAA,MACF;AACA,aAAO,KAAK,KAAK;AACjB,UAAI,IAAI,KAAK,UAAU,KAAK,CAAC,MAAM,IAAK;AAAA,IAC1C,OAAO;AAEL,YAAM,QAAQ;AACd,aAAO,IAAI,KAAK,UAAU,KAAK,CAAC,MAAM,IAAK;AAC3C,aAAO,KAAK,KAAK,MAAM,OAAO,CAAC,CAAC;AAChC,UAAI,IAAI,KAAK,OAAQ;AAAA,IACvB;AAAA,EACF;AAEA,SAAO;AACT;AAKA,SAAS,SAAS,OAA2B;AAC3C,QAAM,aAAa,MAAM,QAAQ,SAAS,IAAI,EAAE,QAAQ,OAAO,IAAI;AACnE,SAAO,WAAW,MAAM,IAAI,EAAE,IAAI,WAAW;AAC/C;AAKA,SAAS,UAAU,UAA0B;AAC3C,QAAM,CAAC,IAAI,IAAI,IAAI,IAAI,SAAS,MAAM,GAAG;AACzC,SAAO,GAAG,IAAI,IAAI,EAAE,IAAI,EAAE;AAC5B;AAQO,IAAM,eAAN,MAAmB;AAAA,EAChB,kBAAkC,CAAC;AAAA,EACnC;AAAA,EACA,cAA2B,CAAC;AAAA,EAEpC,YAAY,UAA0B;AACpC,SAAK,YAAY;AAAA,EACnB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAaA,MAAM,KAA4B;AAChC,SAAK,kBAAkB,CAAC;AACxB,SAAK,cAAc,CAAC;AAGpB,QAAI,OAAO,QAAQ,YAAY,IAAI,SAAS,IAAM,GAAG;AACnD,YAAM,IAAI,WAAW,aAAa;AAAA,IACpC;AAEA,QAAI;AACJ,QAAI;AACF,aAAO,SAAS,GAAG;AAAA,IACrB,QAAQ;AACN,YAAM,IAAI,WAAW,aAAa;AAAA,IACpC;AAEA,QAAI,KAAK,WAAW,GAAG;AACrB,YAAM,IAAI,WAAW,aAAa;AAAA,IACpC;AAGA,UAAM,SAAS,KAAK,CAAC;AACrB,UAAM,WAAmC,CAAC;AAC1C,aAAS,IAAI,GAAG,IAAI,OAAO,QAAQ,KAAK;AACtC,eAAS,OAAO,CAAC,EAAE,KAAK,CAAC,IAAI;AAAA,IAC/B;AAGA,eAAW,OAAO,kBAAkB;AAClC,UAAI,SAAS,GAAG,MAAM,QAAW;AAC/B,cAAM,IAAI,WAAW,kBAAkB,GAAG;AAAA,MAC5C;AAAA,IACF;AAGA,UAAM,WAA0B,KAAK,aAAa,kBAAkB;AAEpE,UAAM,eAA8B,CAAC;AACrC,UAAM,mBAAsC,CAAC;AAC7C,UAAM,wBAAgD,CAAC;AAEvD,aAAS,IAAI,GAAG,IAAI,KAAK,QAAQ,KAAK;AACpC,YAAM,MAAM,KAAK,CAAC;AAElB,YAAM,WAAW,IAAI;AAIrB,UAAI,IAAI,MAAM,CAAC,SAAS,KAAK,KAAK,MAAM,EAAE,EAAG;AAE7C,YAAMC,OAAM,CAAC,SAAyB,IAAI,SAAS,GAAG,CAAC,KAAK,IAAI,KAAK;AAErE,YAAM,OAAOA,KAAI,MAAM;AACvB,YAAM,UAAUA,KAAI,SAAS;AAC7B,YAAM,SAASA,KAAI,UAAU;AAC7B,YAAM,SAAS,WAAW,MAAM;AAChC,YAAM,mBAAmB,WAAW,MAAM,MAAM,MAAM,KAAK,WAAW;AAGtE,UAAI,kBAAkB;AACpB,cAAM,eAAe,QAAQ,YAAY;AACzC,cAAM,eAAe,aAAa,KAAK,CAAC,MAAM,aAAa,SAAS,CAAC,CAAC;AACtE,cAAM,kBACJ,CAAC,gBACD,gBAAgB,KAAK,CAAC,MAAM,aAAa,SAAS,CAAC,CAAC;AAEtD,YAAI,gBAAgB,iBAAiB;AACnC,gBAAM,gBAAgB,WAAWA,KAAI,aAAa,CAAC;AACnD,gBAAM,aAAa,MAAM,aAAa,IAAI,IAAI;AAC9C,gBAAMC,WAAU,UAAUD,KAAI,MAAM,CAAC;AAErC,cAAI,gBAAgB,aAAa,GAAG;AAClC,gBAAI,CAAC,MAAM;AACT,mBAAK,gBAAgB,KAAK;AAAA,gBACxB,MAAM;AAAA,gBACN,KAAK;AAAA,cACP,CAAC;AACD;AAAA,YACF;AACA,kCAAsB,KAAK;AAAA,cACzB;AAAA,cACA,MAAMC;AAAA,cACN,QAAQ,KAAK,IAAI,UAAU;AAAA,cAC3B,KAAK;AAAA,YACP,CAAC;AACD;AAAA,UACF;AAEA,cAAI,mBAAmB,aAAa,GAAG;AACrC,gBAAI,CAAC,MAAM;AACT,mBAAK,gBAAgB,KAAK;AAAA,gBACxB,MAAM;AAAA,gBACN,KAAK;AAAA,cACP,CAAC;AACD;AAAA,YACF;AACA,kBAAM,aAAoC,aAAa;AAAA,cACrD;AAAA,YACF,IACI,aACA;AACJ,6BAAiB,KAAK;AAAA,cACpB;AAAA,cACA;AAAA,cACA,MAAMA;AAAA,cACN;AAAA,cACA;AAAA,cACA,UAAUD,KAAI,sBAAsB;AAAA,cACpC,KAAK;AAAA,YACP,CAAC;AACD;AAAA,UACF;AAAA,QAGF;AAAA,MACF;AAGA,UAAI,CAAC,MAAM;AACT,aAAK,gBAAgB,KAAK,EAAE,MAAM,gBAAgB,KAAK,SAAS,CAAC;AACjE;AAAA,MACF;AAGA,UAAI,MAAM,MAAM,KAAK,WAAW,GAAG;AACjC,aAAK,gBAAgB,KAAK,EAAE,MAAM,iBAAiB,KAAK,SAAS,CAAC;AAClE;AAAA,MACF;AAEA,YAAM,OAAuB,SAAS,IAAI,QAAQ;AAClD,YAAM,WAAW,KAAK,IAAI,MAAM;AAGhC,YAAM,UAAU,UAAUA,KAAI,MAAM,CAAC;AAGrC,YAAM,WAAWA,KAAI,sBAAsB;AAC3C,YAAM,gBAAgB,WAAWA,KAAI,aAAa,CAAC;AACnD,YAAM,aAAa,MAAM,aAAa,IAAI,IAAI;AAE9C,UAAI;AACJ,UAAI;AAEJ,UAAI,aAAa,OAAO;AACtB,mBAAW,KAAK,IAAI,UAAU;AAC9B,iBAAS;AAAA,MACX,OAAO;AACL,cAAM,OAAO,WAAW,UAAU,SAAS,QAAQ;AACnD,YAAI,SAAS,MAAM;AACjB,cAAI,SAAS,QAAQ,MAAM,QAAW;AACpC,iBAAK,gBAAgB,KAAK;AAAA,cACxB,MAAM;AAAA,cACN,KAAK;AAAA,cACL;AAAA,YACF,CAAC;AAAA,UACH,OAAO;AACL,iBAAK,gBAAgB,KAAK;AAAA,cACxB,MAAM;AAAA,cACN,KAAK;AAAA,cACL;AAAA,cACA,MAAM;AAAA,YACR,CAAC;AAAA,UACH;AACA;AAAA,QACF;AACA,mBAAW,KAAK,IAAI,UAAU,IAAI;AAClC,iBAAS;AAAA,MACX;AAGA,YAAM,UAAU,WAAWA,KAAI,mBAAmB,CAAC;AACnD,YAAM,UAAU,KAAK,IAAI,MAAM,OAAO,IAAI,IAAI,OAAO;AAGrD,YAAM,eAAe,WAAWA,KAAI,OAAO,CAAC;AAE5C,mBAAa,KAAK;AAAA,QAChB;AAAA,QACA;AAAA,QACA,MAAM;AAAA,QACN;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF,CAAC;AAAA,IACH;AAIA,UAAM,mBAAmB,oBAAI,IAG3B;AACF,eAAW,KAAK,uBAAuB;AACrC,YAAM,MAAM,GAAG,EAAE,IAAI,IAAI,EAAE,IAAI;AAC/B,YAAM,WAAW,iBAAiB,IAAI,GAAG;AACzC,uBAAiB,IAAI,KAAK;AAAA,QACxB,MAAM,EAAE;AAAA,QACR,MAAM,EAAE;AAAA,QACR,SAAS,UAAU,UAAU,KAAK,EAAE;AAAA,MACtC,CAAC;AAAA,IACH;AACA,UAAM,eAAe,oBAAI,IAAY;AAErC,UAAM,aAA0B,CAAC;AACjC,eAAW,KAAK,kBAAkB;AAChC,YAAM,MAAM,GAAG,EAAE,IAAI,IAAI,EAAE,IAAI;AAC/B,YAAM,qBAAqB,iBAAiB,IAAI,GAAG;AAEnD,UAAI;AACJ,UAAI;AACJ,UAAI;AAEJ,UAAI,EAAE,aAAa,OAAO;AACxB,sBAAc,KAAK,IAAI,EAAE,UAAU;AACnC,yBAAiB,oBAAoB,UAAU;AAC/C,iBAAS;AAAA,MACX,OAAO;AACL,cAAM,OAAO,WAAW,EAAE,UAAU,EAAE,MAAM,QAAQ;AACpD,YAAI,SAAS,MAAM;AACjB,cAAI,SAAS,EAAE,QAAQ,MAAM,QAAW;AACtC,iBAAK,gBAAgB,KAAK;AAAA,cACxB,MAAM;AAAA,cACN,KAAK,EAAE;AAAA,cACP,UAAU,EAAE;AAAA,YACd,CAAC;AAAA,UACH,OAAO;AACL,iBAAK,gBAAgB,KAAK;AAAA,cACxB,MAAM;AAAA,cACN,KAAK,EAAE;AAAA,cACP,UAAU,EAAE;AAAA,cACZ,MAAM,EAAE;AAAA,YACV,CAAC;AAAA,UACH;AACA;AAAA,QACF;AACA,sBAAc,KAAK,IAAI,EAAE,UAAU,IAAI;AACvC,yBAAiB,qBACb,mBAAmB,SAAS,OAC5B;AACJ,iBAAS;AAAA,MACX;AAEA,UAAI,mBAAoB,cAAa,IAAI,GAAG;AAE5C,iBAAW,KAAK;AAAA,QACd,MAAM,EAAE;AAAA,QACR,SAAS,EAAE;AAAA,QACX,MAAM,EAAE;AAAA,QACR,YAAY,EAAE;AAAA,QACd;AAAA,QACA;AAAA,QACA,UAAU,EAAE;AAAA,QACZ;AAAA,MACF,CAAC;AAAA,IACH;AAGA,eAAW,CAAC,KAAK,CAAC,KAAK,kBAAkB;AACvC,UAAI,CAAC,aAAa,IAAI,GAAG,GAAG;AAC1B,aAAK,gBAAgB,KAAK;AAAA,UACxB,MAAM;AAAA,UACN,MAAM,EAAE;AAAA,UACR,MAAM,EAAE;AAAA,QACV,CAAC;AAAA,MACH;AAAA,IACF;AAEA,SAAK,cAAc;AAEnB,WAAO;AAAA,EACT;AAAA,EAEA,IAAI,WAAqB;AACvB,WAAO,KAAK,gBAAgB,IAAI,gBAAgB;AAAA,EAClD;AAAA,EAEA,IAAI,iBAAiC;AACnC,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,IAAI,aAA0B;AAC5B,WAAO,KAAK;AAAA,EACd;AACF;;;AC/ZA,SAAS,iBAAiB;AAY1B,SAAS,YAAY,GAAmB;AACtC,SAAQ,KAAK,KAAK,CAAC,IAAI,KAAK,MAAM,KAAK,IAAI,CAAC,IAAI,GAAG,IAAK;AAC1D;AAEO,SAAS,cACd,SACA,cACA,SACgB;AAChB,QAAM,cAAc,QAAQ;AAC5B,QAAM,eAAe,QAAQ;AAC7B,QAAM,aAAa,YAAY,cAAc,YAAY;AAEzD,MAAI,aAAa,GAAG;AAClB,UAAM,SAAS,CAAC,GAAG,YAAY,EAAE,KAAK,CAAC,GAAG,MAAM,EAAE,OAAO,EAAE,IAAI;AAC/D,QAAI,YAAY;AAChB,UAAM,sBAA2C,CAAC;AAElD,eAAW,SAAS,QAAQ;AAC1B,UAAI,UAAU,MAAM,OAAO,EAAG;AAC9B,YAAM,WAAW,KAAK,IAAI,MAAM,QAAQ,SAAS;AACjD,UAAI,WAAW,GAAG;AAChB,4BAAoB,KAAK;AAAA,UACvB,aAAa,MAAM;AAAA,UACnB,SAAS,YAAY,QAAQ;AAAA,QAC/B,CAAC;AACD,qBAAa;AAAA,MACf;AAAA,IACF;AAEA,UAAM,kBAAkB,YAAY,SAAS;AAC7C,UAAM,UAAU,YAAY,kBAAkB,IAAI;AAElD,WAAO;AAAA,MACL;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,uBAAuB,CAAC;AAAA,IAC1B;AAAA,EACF,WAAW,aAAa,GAAG;AACzB,WAAO;AAAA,MACL;AAAA,MACA;AAAA,MACA;AAAA,MACA,qBAAqB,CAAC;AAAA,MACtB,iBAAiB;AAAA,MACjB,SAAS;AAAA,MACT,uBAAuB;AAAA,QACrB,EAAE,aAAa,SAAS,SAAS,YAAY,KAAK,IAAI,UAAU,CAAC,EAAE;AAAA,MACrE;AAAA,IACF;AAAA,EACF,OAAO;AACL,WAAO;AAAA,MACL;AAAA,MACA;AAAA,MACA,YAAY;AAAA,MACZ,qBAAqB,CAAC;AAAA,MACtB,iBAAiB;AAAA,MACjB,SAAS;AAAA,MACT,uBAAuB,CAAC;AAAA,IAC1B;AAAA,EACF;AACF;AAEO,SAAS,cACd,SACA,YACA,SACgB;AAChB,QAAM,SAAS,SAAS,UAAU,CAAC;AAEnC,QAAM,UAAU,OAAO,KAAK,CAAC,MAAM,EAAE,YAAY,IAAI;AACrD,QAAM,WAAW,OAAO,KAAK,CAAC,MAAM,EAAE,YAAY,KAAK;AAEvD,QAAM,qBAAqB;AAAA,IACzB,aAAa,SAAS,eAAe;AAAA,IACrC,SAAS,SAAS,WAAW;AAAA,EAC/B;AACA,QAAM,sBAAsB;AAAA,IAC1B,aAAa,UAAU,eAAe;AAAA,IACtC,SAAS,UAAU,WAAW;AAAA,EAChC;AAEA,QAAM,kBAAkB,WACrB,OAAO,CAAC,MAAM,EAAE,eAAe,UAAU,EACzC,IAAI,CAAC,OAAO;AAAA,IACX,MAAM,EAAE;AAAA,IACR,UAAU,EAAE;AAAA,IACZ,OAAO,EAAE;AAAA,IACT,iBAAiB,EAAE;AAAA,EACrB,EAAE;AAEJ,QAAM,SAAS,WACZ,OAAO,CAAC,MAAM,EAAE,eAAe,QAAQ,EACvC,IAAI,CAAC,OAAO;AAAA,IACX,MAAM,EAAE;AAAA,IACR,UAAU,EAAE;AAAA,IACZ,SAAS,EAAE;AAAA,IACX,iBAAiB,EAAE;AAAA,EACrB,EAAE;AAEJ,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;AAEO,SAAS,yBACd,UACA,UACA,SACqB;AACrB,QAAM,UAAU;AAChB,QAAM,UAAU;AAChB,QAAM,eAAc,oBAAI,KAAK,GAAE,YAAY;AAE3C,SAAO;AAAA,IACL;AAAA,IACA,aAAa;AAAA,IACb;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,UAAU,OAAOE,UAAiB;AAChC,YAAM;AAAA,QACJA;AAAA,QACA,KAAK;AAAA,UACH;AAAA,YACE;AAAA,YACA,aAAa;AAAA,YACb;AAAA,YACA;AAAA,YACA;AAAA,YACA;AAAA,UACF;AAAA,UACA;AAAA,UACA;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF;;;ACpIA,SAASC,aAAY,GAAmB;AACtC,SAAQ,KAAK,KAAK,CAAC,IAAI,KAAK,MAAM,KAAK,IAAI,CAAC,IAAI,GAAG,IAAK;AAC1D;AAEA,SAAS,aAAa,cAGpB;AACA,QAAM,SAAiC,CAAC;AACxC,aAAW,KAAK,cAAc;AAC5B,UAAM,IAAI,EAAE,KAAK,MAAM,GAAG,CAAC;AAC3B,WAAO,CAAC,KAAK,OAAO,CAAC,KAAK,KAAK;AAAA,EACjC;AACA,QAAM,QAAQ,OAAO,KAAK,MAAM;AAChC,MAAI,MAAM,WAAW;AACnB,WAAO,EAAE,OAAM,oBAAI,KAAK,GAAE,YAAY,GAAG,eAAe,MAAM;AAChE,QAAM,OAAO;AAAA,IACX,MAAM,OAAO,CAAC,GAAG,MAAO,OAAO,CAAC,KAAK,OAAO,CAAC,IAAI,IAAI,CAAE;AAAA,EACzD;AACA,SAAO,EAAE,MAAM,eAAe,MAAM,SAAS,EAAE;AACjD;AAOO,IAAM,aAAN,MAAiB;AAAA,EACL;AAAA,EACA;AAAA,EACA;AAAA,EAEjB,YACE,cACA,eACA,SACA;AACA,SAAK,gBAAgB;AACrB,SAAK,iBAAiB,iBAAiB,CAAC;AACxC,SAAK,WAAW,WAAW,CAAC;AAAA,EAC9B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWA,eAAe,QAAgC;AAC7C,UAAM,WAAqB,CAAC,GAAG,KAAK,cAAc;AAGlD,UAAM,SAAS,CAAC,GAAG,KAAK,aAAa,EAAE,KAAK,CAAC,GAAG,MAAM;AACpD,UAAI,EAAE,OAAO,EAAE,KAAM,QAAO;AAC5B,UAAI,EAAE,OAAO,EAAE,KAAM,QAAO;AAE5B,UAAI,EAAE,SAAS,SAAS,EAAE,SAAS,OAAQ,QAAO;AAClD,UAAI,EAAE,SAAS,UAAU,EAAE,SAAS,MAAO,QAAO;AAClD,aAAO;AAAA,IACT,CAAC;AAGD,UAAM,EAAE,MAAM,SAAS,cAAc,IAAI,aAAa,MAAM;AAC5D,QAAI,eAAe;AACjB,eAAS;AAAA,QACP;AAAA,MACF;AAAA,IACF;AAGA,UAAM,WAAW,oBAAI,IAAmB;AACxC,UAAM,cAA4B,CAAC;AACnC,UAAM,YAAoC,CAAC;AAE3C,eAAW,MAAM,QAAQ;AAEvB,UAAI,GAAG,WAAW,QAAW;AAC3B,kBAAU,GAAG,GAAG,QAAQ,IAAI,GAAG,IAAI,EAAE,IAAI,GAAG;AAAA,MAC9C;AAEA,UAAI,GAAG,SAAS,OAAO;AACrB,cAAM,MAAW;AAAA,UACf,MAAM,GAAG;AAAA,UACT,UAAU,GAAG;AAAA,UACb,iBAAiB,GAAG,WAAW,GAAG;AAAA,UAClC,SAAS,GAAG;AAAA,UACZ,aAAa,GAAG;AAAA,UAChB,QAAQ,GAAG;AAAA,QACb;AACA,YAAI,CAAC,SAAS,IAAI,GAAG,IAAI,EAAG,UAAS,IAAI,GAAG,MAAM,CAAC,CAAC;AACpD,iBAAS,IAAI,GAAG,IAAI,EAAG,KAAK,GAAG;AAAA,MACjC,OAAO;AAEL,cAAM,OAAO,SAAS,IAAI,GAAG,IAAI;AACjC,YAAI,CAAC,QAAQ,KAAK,WAAW,GAAG;AAC9B,gBAAM,IAAI,iBAAiB,GAAG,MAAM,GAAG,IAAI;AAAA,QAC7C;AAEA,cAAM,sBAAsB,GAAG,WAAW,GAAG;AAC7C,YAAI,mBAAmB,GAAG;AAE1B,eAAO,mBAAmB,GAAG;AAC3B,cAAI,CAAC,QAAQ,KAAK,WAAW,GAAG;AAC9B,kBAAM,IAAI,iBAAiB,GAAG,MAAM,GAAG,IAAI;AAAA,UAC7C;AAEA,gBAAM,MAAM,WAAW,SAAS,KAAK,KAAK,SAAS,CAAC,IAAI,KAAK,CAAC;AAC9D,gBAAM,aAAa,KAAK,IAAI,IAAI,UAAU,gBAAgB;AAG1D,gBAAM,sBACJ,IAAI,WAAW,aAAa,IAAI;AAClC,gBAAM,uBAAuB,GAAG,WAAW,aAAa,GAAG;AAE3D,gBAAM,aACJ,IAAI,kBAAkB,aAAa;AACrC,gBAAM,kBACJ,sBAAsB,aAAa;AACrC,gBAAM,cAAc,kBAAkB;AAEtC,sBAAY,KAAK;AAAA,YACf,MAAM,GAAG;AAAA,YACT,SAAS,GAAG;AAAA,YACZ,UAAU;AAAA,YACV,SAAS,IAAI;AAAA,YACb,UAAU,GAAG;AAAA,YACb,aAAa,IAAI;AAAA,YACjB,cAAc;AAAA,YACd,YAAYA,aAAY,UAAU;AAAA,YAClC,iBAAiBA,aAAY,eAAe;AAAA,YAC5C,aAAaA,aAAY,WAAW;AAAA,YACpC,WAAW,IAAI;AAAA,YACf,YAAY,GAAG;AAAA,UACjB,CAAC;AAED,cAAI,YAAY;AAChB,8BAAoB;AAEpB,cAAI,IAAI,YAAY,GAAG;AACrB,gBAAI,WAAW,QAAQ;AACrB,mBAAK,IAAI;AAAA,YACX,OAAO;AACL,mBAAK,MAAM;AAAA,YACb;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAGA,QAAI,cAAc;AAClB,QAAI,eAAe;AACnB,eAAW,OAAO,aAAa;AAC7B,UAAI,IAAI,cAAc,EAAG,gBAAe,IAAI;AAAA,UACvC,iBAAgB,KAAK,IAAI,IAAI,WAAW;AAAA,IAC/C;AAGA,QAAI,KAAK,SAAS,gBAAgB;AAChC,YAAM,iBAAiB,KAAK,SAAS;AACrC,YAAM,oBAAoB,oBAAI,IAAY;AAE1C,iBAAW,OAAO,aAAa;AAC7B,cAAM,QAAQ,eAAe,IAAI,IAAI;AACrC,YAAI,CAAC,OAAO;AACV,cAAI,SAAS;AACb,4BAAkB,IAAI,IAAI,IAAI;AAAA,QAChC,WAAW,MAAM,eAAe,OAAO,IAAI,eAAe,GAAG;AAC3D,cAAI,SAAS;AAAA,QACf,OAAO;AACL,cAAI,SAAS;AAAA,QACf;AAAA,MACF;AAEA,iBAAW,QAAQ,mBAAmB;AACpC,iBAAS;AAAA,UACP,QAAQ,IAAI;AAAA,QACd;AAAA,MACF;AAGA,YAAM,cAAc,YAAY,OAAO,CAAC,MAAM,EAAE,WAAW,GAAG;AAC9D,YAAM,eAAe,oBAAI,IAGvB;AACF,iBAAW,OAAO,aAAa;AAC7B,cAAM,QAAQ,eAAe,IAAI,IAAI;AACrC,cAAM,OAAO,MAAM;AACnB,YAAI,CAAC,aAAa,IAAI,IAAI;AACxB,uBAAa,IAAI,MAAM,EAAE,cAAc,oBAAI,IAAI,GAAG,aAAa,EAAE,CAAC;AACpE,cAAM,IAAI,aAAa,IAAI,IAAI;AAC/B,UAAE,aAAa,IAAI,MAAM,UAAU;AACnC,UAAE,eAAe,IAAI;AAAA,MACvB;AACA,YAAM,gBAAgB,CAAC,GAAG,aAAa,QAAQ,CAAC,EAAE,IAAI,CAAC,CAAC,SAAS,CAAC,OAAO;AAAA,QACvE;AAAA,QACA,cAAc,CAAC,GAAG,EAAE,YAAY;AAAA,QAChC,aAAaA,aAAY,EAAE,WAAW;AAAA,QACtC,SAASA,aAAY,EAAE,cAAc,OAAO;AAAA,MAC9C,EAAE;AACF,YAAM,gBAA+B;AAAA,QACnC,QAAQ;AAAA,QACR,cAAcA;AAAA,UACZ,cAAc,OAAO,CAAC,GAAG,MAAM,IAAI,EAAE,SAAS,CAAC;AAAA,QACjD;AAAA,MACF;AAGA,YAAM,cAAc,YAAY,OAAO,CAAC,MAAM,EAAE,WAAW,GAAG;AAC9D,UAAI,eAAe;AACnB,UAAI,gBAAgB;AACpB,iBAAW,OAAO,aAAa;AAC7B,YAAI,IAAI,eAAe,EAAG,iBAAgB,IAAI;AAAA,YACzC,kBAAiB,KAAK,IAAI,IAAI,WAAW;AAAA,MAChD;AACA,qBAAeA,aAAY,YAAY;AACvC,sBAAgBA,aAAY,aAAa;AAEzC,YAAM,gBAAgB,CAAC,GAAI,KAAK,SAAS,gBAAgB,CAAC,CAAE,EAAE;AAAA,QAC5D,CAAC,GAAG,MAAM,EAAE,OAAO,EAAE;AAAA,MACvB;AACA,UAAI,YAAY,eAAe;AAC/B,UAAI,sBAAsB;AAC1B,iBAAW,SAAS,eAAe;AACjC,YAAI,UAAU,MAAM,OAAO,EAAG;AAC9B,YAAI,aAAa,EAAG;AACpB,cAAM,WAAW,KAAK,IAAI,MAAM,QAAQ,SAAS;AACjD,+BAAuB;AACvB,qBAAa;AAAA,MACf;AACA,4BAAsBA,aAAY,mBAAmB;AACrD,YAAM,aAAaA;AAAA,QACjB,eAAe,gBAAgB;AAAA,MACjC;AACA,YAAM,wBAAwBA,aAAY,KAAK,IAAI,GAAG,CAAC,UAAU,CAAC;AAElE,YAAM,gBAA+B;AAAA,QACnC,aAAa;AAAA,QACb,cAAc;AAAA,QACd;AAAA,QACA;AAAA,QACA,WAAW;AAAA,MACb;AAGA,YAAM,gBAAgB,KAAK,SAAS,cAAc,CAAC;AACnD,YAAM,qBAAqB,cAAc;AAAA,QACvC,CAAC,QAAQ,IAAI,KAAK,IAAI,IAAI,EAAE,YAAY,MAAM;AAAA,MAChD;AACA,UAAI,mBAAmB,SAAS,cAAc,QAAQ;AACpD,iBAAS,KAAK,gCAAgC,OAAO,gBAAgB;AAAA,MACvE;AAEA,YAAM,WAAW;AAAA,QACf;AAAA,QACA,KAAK,SAAS,gBAAgB,CAAC;AAAA,QAC/B;AAAA,MACF;AACA,YAAM,WAAW;AAAA,QACf,cAAc,SAAS,IAAI,gBAAgB;AAAA,QAC3C;AAAA,QACA;AAAA,MACF;AACA,YAAM,sBAAsB;AAAA,QAC1B;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAEA,aAAO;AAAA,QACL;AAAA,QACA;AAAA,QACA,aAAaA,aAAY,WAAW;AAAA,QACpC,cAAcA,aAAY,YAAY;AAAA,QACtC,WAAWA,aAAY,cAAc,YAAY;AAAA,QACjD,MAAM;AAAA,QACN;AAAA,QACA;AAAA,QACA,cAAa,oBAAI,KAAK,GAAE,YAAY;AAAA,QACpC,SAAS,cAAc,SAAS,IAAI,gBAAgB;AAAA,QACpD,SAAS;AAAA,QACT,eAAe;AAAA,MACjB;AAAA,IACF,OAAO;AAEL,YAAM,WAAW,CAAC,GAAG,IAAI,IAAI,KAAK,cAAc,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;AACnE,YAAM,cAAc,SAAS,KAAK,CAAC,SAAS,KAAK,WAAW,IAAI,CAAC;AACjE,YAAM,WAAW,SAAS,KAAK,CAAC,SAAS,CAAC,KAAK,WAAW,IAAI,CAAC;AAC/D,UAAI,eAAe,UAAU;AAC3B,iBAAS;AAAA,UACP;AAAA,QAGF;AAAA,MACF;AAAA,IACF;AAEA,WAAO;AAAA,MACL;AAAA,MACA;AAAA,MACA,aAAaA,aAAY,WAAW;AAAA,MACpC,cAAcA,aAAY,YAAY;AAAA,MACtC,WAAWA,aAAY,cAAc,YAAY;AAAA,MACjD,MAAM;AAAA,MACN;AAAA,MACA;AAAA,MACA,cAAa,oBAAI,KAAK,GAAE,YAAY;AAAA,IACtC;AAAA,EACF;AACF;;;AC3UA,YAAYC,SAAQ;AACpB,YAAY,WAAW;AAQvB,SAAS,UACP,KACA,MACA,WAC2C;AAC3C,SAAO,IAAI,QAAQ,CAACC,UAAS,WAAW;AACtC,UAAM,IAAI,IAAI,IAAI,GAAG;AACrB,UAAM,MAAY;AAAA,MAChB;AAAA,QACE,UAAU,EAAE;AAAA,QACZ,MAAM,EAAE,WAAW,EAAE;AAAA,QACrB,QAAQ;AAAA,QACR,SAAS;AAAA,UACP,gBAAgB;AAAA,UAChB,kBAAkB,OAAO,WAAW,IAAI;AAAA,QAC1C;AAAA,MACF;AAAA,MACA,CAAC,QAAQ;AACP,YAAI,OAAO;AACX,YAAI,GAAG,QAAQ,CAAC,UAAU;AACxB,kBAAQ;AAAA,QACV,CAAC;AACD,YAAI,GAAG,OAAO,MAAMA,SAAQ,EAAE,QAAQ,IAAI,cAAc,GAAG,KAAK,CAAC,CAAC;AAAA,MACpE;AAAA,IACF;AACA,QAAI,WAAW,WAAW,MAAM;AAC9B,UAAI,QAAQ;AACZ,aAAO,IAAI,MAAM,SAAS,CAAC;AAAA,IAC7B,CAAC;AACD,QAAI,GAAG,SAAS,MAAM;AACtB,QAAI,MAAM,IAAI;AACd,QAAI,IAAI;AAAA,EACV,CAAC;AACH;AAEA,IAAM,sBAAsB,oBAAI,IAAI;AAAA,EAClC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAUD,IAAM,oBAAiD;AAAA,EACrD,KAAK;AAAA,IACH,YAAY;AAAA,IACZ,YAAY;AAAA,IACZ,YAAY;AAAA,IACZ,SAAS;AAAA,IACT,aAAa;AAAA,EACf;AAAA,EACA,KAAK;AAAA,IACH,YAAY;AAAA,IACZ,YAAY;AAAA,IACZ,YAAY;AAAA,IACZ,SAAS;AAAA,IACT,aAAa;AAAA,EACf;AAAA,EACA,eAAe;AAAA,IACb,YAAY;AAAA,IACZ,YAAY;AAAA,IACZ,YAAY;AAAA,IACZ,SAAS;AAAA,IACT,aAAa;AAAA,EACf;AAAA,EACA,iBAAiB;AAAA,IACf,YAAY;AAAA,IACZ,YAAY;AAAA,IACZ,YAAY;AAAA,IACZ,SAAS;AAAA,IACT,aAAa;AAAA,EACf;AAAA,EACA,gBAAgB;AAAA,IACd,YAAY;AAAA,IACZ,YAAY;AAAA,IACZ,YAAY;AAAA,IACZ,SAAS;AAAA,IACT,aAAa;AAAA,EACf;AAAA,EACA,QAAQ;AAAA,IACN,YAAY;AAAA,IACZ,YAAY;AAAA,IACZ,YAAY;AAAA,IACZ,SAAS;AAAA,IACT,aAAa;AAAA,EACf;AAAA,EACA,6BAA6B;AAAA,IAC3B,YAAY;AAAA,IACZ,YAAY;AAAA,IACZ,YAAY;AAAA,IACZ,SAAS;AAAA,IACT,aAAa;AAAA,EACf;AAAA,EACA,kBAAkB;AAAA,IAChB,YAAY;AAAA,IACZ,YAAY;AAAA,IACZ,YAAY;AAAA,IACZ,SAAS;AAAA,IACT,aAAa;AAAA,EACf;AAAA,EACA,QAAQ;AAAA,IACN,YAAY;AAAA,IACZ,YAAY;AAAA,IACZ,YAAY;AAAA,IACZ,SAAS;AAAA,IACT,aAAa;AAAA,EACf;AAAA,EACA,QAAQ;AAAA,IACN,YAAY;AAAA,IACZ,YAAY;AAAA,IACZ,YAAY;AAAA,IACZ,SAAS;AAAA,IACT,aAAa;AAAA,EACf;AAAA,EACA,SAAS;AAAA,IACP,YAAY;AAAA,IACZ,YAAY;AAAA,IACZ,YAAY;AAAA,IACZ,SAAS;AAAA,IACT,aAAa;AAAA,EACf;AAAA,EACA,KAAK;AAAA,IACH,YAAY;AAAA,IACZ,YAAY;AAAA,IACZ,YAAY;AAAA,IACZ,SAAS;AAAA,IACT,aAAa;AAAA,EACf;AAAA,EACA,MAAM;AAAA,IACJ,YAAY;AAAA,IACZ,YAAY;AAAA,IACZ,YAAY;AAAA,IACZ,SAAS;AAAA,IACT,aAAa;AAAA,EACf;AAAA,EACA,wBAAwB;AAAA,IACtB,YAAY;AAAA,IACZ,YAAY;AAAA,IACZ,YAAY;AAAA,IACZ,SAAS;AAAA,IACT,aAAa;AAAA,EACf;AAAA,EACA,qBAAqB;AAAA,IACnB,YAAY;AAAA,IACZ,YAAY;AAAA,IACZ,YAAY;AAAA,IACZ,SAAS;AAAA,IACT,aAAa;AAAA,EACf;AAAA,EACA,iCAAiC;AAAA,IAC/B,YAAY;AAAA,IACZ,YAAY;AAAA,IACZ,YAAY;AAAA,IACZ,SAAS;AAAA,IACT,aAAa;AAAA,EACf;AACF;AAEA,SAAS,YAAY,GAAoB;AACvC,SAAO,MAAM,qBAAqB,KAAK;AACzC;AAEA,SAAS,eACP,MACA,cACA,SACA,UACqB;AACrB,MAAI,iBAAiB,mBAAmB;AACtC,UAAM,SAAS,KAAK,MAAM,GAAG,CAAC,EAAE,YAAY;AAC5C,UAAM,cAAc,oBAAoB,IAAI,MAAM;AAClD,WAAO;AAAA,MACL;AAAA,MACA,YAAY,cAAc,eAAe;AAAA,MACzC,YAAY;AAAA,MACZ,YAAY;AAAA,MACZ,SAAS,cAAc,QAAQ;AAAA,MAC/B;AAAA,MACA,iBAAiB;AAAA,MACjB,QAAQ;AAAA,IACV;AAAA,EACF;AAEA,QAAM,SAAS,kBAAkB,YAAY;AAC7C,MAAI,QAAQ;AACV,WAAO;AAAA,MACL;AAAA,MACA,YAAY,OAAO;AAAA,MACnB,YAAY,OAAO;AAAA,MACnB,YAAY,OAAO;AAAA,MACnB,SAAS,OAAO;AAAA,MAChB,aAAa,OAAO;AAAA,MACpB,iBAAiB;AAAA,MACjB,QAAQ;AAAA,IACV;AAAA,EACF;AAGA,WAAS;AAAA,IACP,yBAAyB,IAAI,KAAK,YAAY;AAAA,EAChD;AACA,SAAO;AAAA,IACL;AAAA,IACA,YAAY;AAAA,IACZ,YAAY;AAAA,IACZ,YAAY;AAAA,IACZ,SAAS;AAAA,IACT,aAAa;AAAA,IACb,iBAAiB;AAAA,IACjB,QAAQ;AAAA,EACV;AACF;AAEA,IAAM,uBAAwD;AAAA,EAC5D,KAAK;AAAA,IACH,YAAY;AAAA,IACZ,YAAY;AAAA,IACZ,YAAY;AAAA,IACZ,SAAS;AAAA,IACT,aAAa;AAAA,EACf;AAAA,EACA,OAAO;AAAA,IACL,YAAY;AAAA,IACZ,YAAY;AAAA,IACZ,YAAY;AAAA,IACZ,SAAS;AAAA,IACT,aAAa;AAAA,EACf;AAAA,EACA,KAAK;AAAA,IACH,YAAY;AAAA,IACZ,YAAY;AAAA,IACZ,YAAY;AAAA,IACZ,SAAS;AAAA,IACT,aAAa;AAAA,EACf;AAAA,EACA,YAAY;AAAA,IACV,YAAY;AAAA,IACZ,YAAY;AAAA,IACZ,YAAY;AAAA,IACZ,SAAS;AAAA,IACT,aAAa;AAAA,EACf;AAAA,EACA,eAAe;AAAA,IACb,YAAY;AAAA,IACZ,YAAY;AAAA,IACZ,YAAY;AAAA,IACZ,SAAS;AAAA,IACT,aAAa;AAAA,EACf;AAAA,EACA,UAAU;AAAA,IACR,YAAY;AAAA,IACZ,YAAY;AAAA,IACZ,YAAY;AAAA,IACZ,SAAS;AAAA,IACT,aAAa;AAAA,EACf;AAAA,EACA,YAAY;AAAA,IACV,YAAY;AAAA,IACZ,YAAY;AAAA,IACZ,YAAY;AAAA,IACZ,SAAS;AAAA,IACT,aAAa;AAAA,EACf;AAAA,EACA,cAAc;AAAA,IACZ,YAAY;AAAA,IACZ,YAAY;AAAA,IACZ,YAAY;AAAA,IACZ,SAAS;AAAA,IACT,aAAa;AAAA,EACf;AAAA,EACA,kBAAkB;AAAA,IAChB,YAAY;AAAA,IACZ,YAAY;AAAA,IACZ,YAAY;AAAA,IACZ,SAAS;AAAA,IACT,aAAa;AAAA,EACf;AACF;AAQA,SAAS,yBACP,YACA,SACqB;AACrB,QAAM,SAAS,qBAAqB,UAAU;AAC9C,SAAO;AAAA,IACL;AAAA,IACA,YAAY,OAAO;AAAA,IACnB,YAAY,OAAO;AAAA,IACnB,YAAY,OAAO;AAAA,IACnB,SAAS,OAAO;AAAA,IAChB,aAAa,OAAO;AAAA,IACpB,iBAAiB;AAAA,IACjB,QAAQ;AAAA,EACV;AACF;AAKO,IAAM,aAAN,MAAiB;AAAA,EACL;AAAA,EAEjB,YAAY,SAAqC;AAC/C,SAAK,cAAc,SAAS,eAAe;AAAA,EAC7C;AAAA,EAEA,MAAM,KAAK,aAAiD;AAC1D,QAAI,CAAI,eAAW,WAAW,GAAG;AAC/B,YAAM,IAAI,oBAAoB,mBAAmB;AAAA,IACnD;AAEA,QAAI;AACJ,QAAI;AACF,YAAM,MAAS,iBAAa,aAAa,OAAO;AAChD,eAAS,KAAK,MAAM,GAAG;AAAA,IACzB,QAAQ;AACN,YAAM,IAAI,oBAAoB,mBAAmB;AAAA,IACnD;AAEA,QACE,OAAO,WAAW,YAClB,WAAW,QACV,OAAmC,SAAS,MAAM,GACnD;AACA,YAAM,IAAI,oBAAoB,iBAAiB;AAAA,IACjD;AAEA,WAAQ,OAAkD;AAAA,EAC5D;AAAA,EAEA,MAAM,SACJ,cACA,aACA,SACA,YAAsB,WACM;AAE5B,UAAM,gBAAgB,oBAAI,IAAoB;AAC9C,eAAW,MAAM,cAAc;AAC7B,UAAI,GAAG,QAAQ,CAAC,cAAc,IAAI,GAAG,IAAI,GAAG;AAC1C,sBAAc,IAAI,GAAG,MAAM,GAAG,OAAO;AAAA,MACvC;AAAA,IACF;AAQA,UAAM,YAA+B,CAAC;AAEtC,QAAI,gBAAgB,QAAW;AAC7B,UAAO,eAAW,WAAW,GAAG;AAC9B,cAAM,cAAc,MAAM,KAAK,KAAK,WAAW;AAC/C,mBAAW,CAAC,MAAM,KAAK,KAAK,OAAO,QAAQ,WAAW,GAAG;AACvD,cAAI,MAAM,iBAAiB;AACzB,sBAAU,IAAI,IAAI;AAAA,UACpB;AAAA,QACF;AAAA,MACF;AAAA,IACF,WAAW,SAAS,wBAAwB;AAC1C,aAAO,OAAO,WAAW,QAAQ,sBAAsB;AAAA,IACzD;AAGA,eAAW,CAAC,MAAM,UAAU,KAAK,OAAO,QAAQ,SAAS,aAAa,CAAC,CAAC,GAAG;AACzE,YAAM,UAAU,cAAc,IAAI,IAAI,KAAK;AAC3C,gBAAU,IAAI,IAAI,yBAAyB,YAAY,OAAO;AAAA,IAChE;AAGA,UAAM,YAAsB,CAAC;AAC7B,eAAW,QAAQ,cAAc,KAAK,GAAG;AACvC,UAAI,EAAE,QAAQ,YAAY;AACxB,kBAAU,KAAK,IAAI;AAAA,MACrB;AAAA,IACF;AAGA,UAAM,WAAqB,CAAC;AAC5B,UAAM,aAAgC,CAAC;AAEvC,QAAI,SAAS,SAAS;AACpB,iBAAW,QAAQ,WAAW;AAC5B,cAAM,UAAU,cAAc,IAAI,IAAI,KAAK;AAC3C,iBAAS;AAAA,UACP,yBAAyB,IAAI;AAAA,QAC/B;AACA,mBAAW,IAAI,IAAI;AAAA,UACjB;AAAA,UACA,YAAY;AAAA,UACZ,YAAY;AAAA,UACZ,YAAY;AAAA,UACZ,SAAS;AAAA,UACT,aAAa;AAAA,UACb,iBAAiB;AAAA,UACjB,QAAQ;AAAA,QACV;AAAA,MACF;AAAA,IACF,OAAO;AACL,YAAM,aAAa;AACnB,YAAM,eAAe,KAAK,KAAK,UAAU,SAAS,UAAU;AAE5D,eAAS,IAAI,GAAG,IAAI,UAAU,QAAQ,KAAK,YAAY;AAErD,YAAI,IAAI,GAAG;AACT,gBAAM,IAAI,QAAc,CAACA,aAAY,WAAWA,UAAS,GAAI,CAAC;AAAA,QAChE;AAEA,cAAM,QAAQ,UAAU,MAAM,GAAG,IAAI,UAAU;AAC/C,cAAM,cAAc,KAAK;AAAA,UACvB,MAAM,IAAI,CAAC,UAAU,EAAE,QAAQ,WAAW,SAAS,KAAK,EAAE;AAAA,QAC5D;AAEA,YAAI,WAAoD;AAGxD,iBAAS,UAAU,GAAG,UAAU,GAAG,WAAW;AAC5C,cAAI;AACF,kBAAM,IAAI,MAAM;AAAA,cACd;AAAA,cACA;AAAA,cACA;AAAA,YACF;AACA,gBAAI,EAAE,SAAS,KAAK;AAClB,yBAAW;AACX;AAAA,YACF;AAAA,UAEF,QAAQ;AAEN,kBAAM,IAAI,oBAAoB,eAAe;AAAA,UAC/C;AAAA,QACF;AAEA,YAAI,aAAa,MAAM;AACrB,gBAAM,IAAI,oBAAoB,eAAe;AAAA,QAC/C;AAEA,cAAM,UAAU,KAAK,MAAM,SAAS,IAAI;AAExC,iBAAS,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;AACrC,gBAAM,OAAO,MAAM,CAAC;AACpB,gBAAM,SAAS,QAAQ,CAAC;AACxB,gBAAM,UAAU,cAAc,IAAI,IAAI,KAAK;AAE3C,cACE,CAAC,UACD,OAAO,SACP,CAAC,OAAO,QACR,OAAO,KAAK,WAAW,GACvB;AACA,qBAAS;AAAA,cACP,yBAAyB,IAAI;AAAA,YAC/B;AACA,uBAAW,IAAI,IAAI;AAAA,cACjB;AAAA,cACA,YAAY;AAAA,cACZ,YAAY;AAAA,cACZ,YAAY;AAAA,cACZ,SAAS;AAAA,cACT,aAAa;AAAA,cACb,iBAAiB;AAAA,cACjB,QAAQ;AAAA,YACV;AACA;AAAA,UACF;AAEA,gBAAM,KAAK,OAAO,KAAK,CAAC,GAAG;AAC3B,gBAAM,MAAM,OAAO,KAAK,CAAC,GAAG;AAG5B,gBAAM,YACJ,MAAM,YAAY,EAAE,IAChB,KACA,OAAO,YAAY,GAAG,IACpB,MACC,MAAM,OAAO;AAEtB,qBAAW,IAAI,IAAI,eAAe,MAAM,WAAW,SAAS,QAAQ;AAAA,QACtE;AAEA,cAAM,aAAa,IAAI;AACvB,iBAAS,kBAAkB,aAAa,GAAG,YAAY;AAAA,MACzD;AAAA,IACF;AAGA,UAAM,YAA+B,EAAE,GAAG,YAAY,GAAG,UAAU;AAGnE,QAAI,gBAAgB,QAAW;AAC7B,YAAM,iBAAiB,KAAK;AAAA,QAC1B;AAAA,UACE,SAAS;AAAA,UACT,cAAa,oBAAI,KAAK,GAAE,YAAY;AAAA,UACpC,iBAAiB;AAAA,UACjB;AAAA,QACF;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAEA,UAAI;AACF,QAAG,kBAAc,aAAa,gBAAgB,OAAO;AAAA,MACvD,QAAQ;AACN,cAAM,IAAI,oBAAoB,aAAa;AAAA,MAC7C;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AACF;;;ACxjBA,IAAM,YAAY,SAAI,OAAO,EAAE;AAE/B,SAAS,UAAU,QAAgB,QAAwB;AACzD,SAAO,IAAI,KAAK,aAAa,QAAQ;AAAA,IACnC,uBAAuB;AAAA,IACvB,uBAAuB;AAAA,EACzB,CAAC,EAAE,OAAO,MAAM;AAClB;AAEA,SAAS,eAAe,QAAgB,QAAwB;AAC9D,QAAM,YAAY,UAAU,KAAK,IAAI,MAAM,GAAG,MAAM;AACpD,SAAO,UAAU,IAAI,IAAI,SAAS,KAAK,IAAI,SAAS;AACtD;AAEA,SAAS,gBAAgB,IAAgB,GAA0B;AACjE,MAAI,OAAO,MAAO,QAAO,EAAE;AAC3B,MAAI,OAAO,gBAAgB,OAAO,gBAAiB,QAAO,EAAE;AAC5D,SAAO;AACT;AAOA,SAAS,oBACP,QACA,GACA,yBACQ;AACR,QAAM,IAAI,OAAO;AACjB,QAAM,KAAK,EAAE;AACb,QAAM,KAAK,EAAE;AACb,QAAM,MAAM,CAAC,MAAc,UAAU,GAAG,EAAE,YAAY;AACtD,QAAM,QAAkB,CAAC;AAEzB,QAAM,KAAK,SAAS;AACpB,QAAM,KAAK,EAAE,oBAAoB,EAAE,WAAW,CAAC;AAC/C,QAAM,KAAK,EAAE,iBAAiB;AAC9B,QAAM,KAAK,EAAE;AAGb,QAAM,KAAK,EAAE,cAAc;AAC3B,QAAM,KAAK,KAAK,EAAE,mBAAmB,KAAK,IAAI,GAAG,WAAW,CAAC,EAAE;AAC/D,QAAM,KAAK,KAAK,EAAE,oBAAoB,KAAK,IAAI,GAAG,YAAY,CAAC,EAAE;AACjE,QAAM,KAAK,KAAK,EAAE,kBAAkB,KAAK,IAAI,GAAG,UAAU,CAAC,EAAE;AAC7D,aAAW,SAAS,GAAG,qBAAqB;AAC1C,UAAM;AAAA,MACJ,KAAK,EAAE,gBAAgB,MAAM,WAAW,CAAC,KAAK,IAAI,MAAM,OAAO,CAAC;AAAA,IAClE;AAAA,EACF;AACA,MAAI,UAAU,KAAK,EAAE,kBAAkB,KAAK,IAAI,GAAG,eAAe,CAAC;AACnE,MAAI,GAAG,aAAa,GAAG;AACrB,eAAW,OAAO,EAAE,eAAe,IAAI,IAAI,KAAK,IAAI,GAAG,UAAU,CAAC,CAAC;AAAA,EACrE;AACA,MAAI,GAAG,aAAa,KAAK,CAAC,yBAAyB;AACjD,eAAW,IAAI,EAAE,0BAA0B;AAAA,EAC7C;AACA,QAAM,KAAK,OAAO;AAClB,QAAM,KAAK,KAAK,EAAE,eAAe,KAAK,IAAI,GAAG,OAAO,CAAC,EAAE;AACvD,MAAI,GAAG,aAAa,GAAG;AACrB,UAAM,YAAY,GAAG,sBAAsB;AAAA,MACzC,CAAC,KAAK,MAAM,MAAM,EAAE;AAAA,MACpB;AAAA,IACF;AACA,UAAM;AAAA,MACJ,KAAK,EAAE,mBAAmB,KAAK,IAAI,SAAS,CAAC,KAAK,EAAE,gBAAgB;AAAA,IACtE;AAAA,EACF;AACA,QAAM,KAAK,EAAE;AAGb,QAAM,KAAK,EAAE,cAAc;AAC3B,MAAI,GAAG,mBAAmB,cAAc,GAAG;AACzC,UAAM;AAAA,MACJ,KAAK,EAAE,aAAa,KAAK,IAAI,GAAG,mBAAmB,WAAW,CAAC;AAAA,IACjE;AACA,UAAM;AAAA,MACJ,aAAa,EAAE,eAAe,KAAK,IAAI,GAAG,mBAAmB,OAAO,CAAC;AAAA,IACvE;AAAA,EACF;AACA,MAAI,GAAG,oBAAoB,cAAc,GAAG;AAC1C,UAAM;AAAA,MACJ,KAAK,EAAE,WAAW,KAAK,IAAI,GAAG,oBAAoB,WAAW,CAAC;AAAA,IAChE;AACA,UAAM;AAAA,MACJ,aAAa,EAAE,eAAe,KAAK,IAAI,GAAG,oBAAoB,OAAO,CAAC;AAAA,IACxE;AAAA,EACF;AACA,aAAW,OAAO,GAAG,iBAAiB;AACpC,UAAM;AAAA,MACJ,KAAK,EAAE,iBAAiB,WAAM,IAAI,QAAQ,KAAK,IAAI,IAAI,MAAM,IAAI,IAAI,KAAK,CAAC;AAAA,IAC7E;AACA,UAAM;AAAA,MACJ,aAAa,EAAE,gBAAgB,WAAM,IAAI,QAAQ,KAAK,IAAI,IAAI,MAAM,IAAI,IAAI,eAAe,CAAC;AAAA,IAC9F;AAAA,EACF;AACA,aAAW,KAAK,GAAG,QAAQ;AACzB,UAAM;AAAA,MACJ,KAAK,EAAE,cAAc,WAAM,EAAE,QAAQ,KAAK,EAAE,IAAI,MAAM,IAAI,EAAE,OAAO,CAAC;AAAA,IACtE;AAAA,EACF;AACA,QAAM,KAAK,EAAE;AAEb,QAAM,KAAK,EAAE,uBAAuB;AACpC,QAAM,KAAK,EAAE,uBAAuB;AAEpC,SAAO,MAAM,KAAK,IAAI;AACxB;AAEO,SAAS,aACd,QACA,GACA,yBACQ;AACR,QAAM,MAAM,CAAC,MAAc,UAAU,GAAG,EAAE,YAAY;AACtD,QAAM,QAAkB,CAAC;AAEzB,QAAM,aAAa,CAAC,EAAE,OAAO,WAAW,OAAO;AAG/C,QAAM;AAAA,IACJ,GAAG,EAAE,YAAY,KAAK,OAAO,MAAM,MAAM,EAAE,aAAa,KAAK,OAAO,OAAO;AAAA,EAC7E;AACA,QAAM,KAAK,EAAE;AAGb,aAAW,KAAK,OAAO,UAAU;AAC/B,QAAI,EAAE,WAAW,SAAS,KAAK,EAAE,WAAW,UAAU,GAAG;AACvD,YAAM,KAAK,CAAC;AACZ,YAAM,KAAK,EAAE;AAAA,IACf;AAAA,EACF;AAGA,QAAM,aAAa;AAAA,IACjB,EAAE,WAAW,OAAO,EAAE;AAAA,IACtB,EAAE,cAAc,OAAO,EAAE;AAAA,IACzB,EAAE,UAAU,SAAS,CAAC;AAAA,IACtB,EAAE,cAAc,OAAO,EAAE;AAAA,IACzB,EAAE,eAAe,OAAO,EAAE;AAAA,IAC1B,EAAE,aAAa,SAAS,EAAE;AAAA,IAC1B,EAAE,cAAc,SAAS,EAAE;AAAA,IAC3B,EAAE,eAAe,SAAS,EAAE;AAAA,EAC9B;AACA,MAAI,YAAY;AACd,eAAW,KAAK,EAAE,aAAa,SAAS,CAAC,CAAC;AAAA,EAC5C;AACA,QAAM,KAAK,WAAW,KAAK,IAAI,CAAC;AAGhC,aAAW,OAAO,OAAO,MAAM;AAC7B,UAAM,UAAU;AAAA,MACd,IAAI,KAAK,OAAO,EAAE;AAAA,MAClB,IAAI,QAAQ,UAAU,GAAG,EAAE,EAAE,OAAO,EAAE;AAAA,MACtC,OAAO,IAAI,QAAQ,EAAE,SAAS,CAAC;AAAA,MAC/B,IAAI,QAAQ,OAAO,EAAE;AAAA,MACrB,IAAI,SAAS,OAAO,EAAE;AAAA,MACtB,IAAI,IAAI,UAAU,EAAE,SAAS,EAAE;AAAA,MAC/B,IAAI,IAAI,eAAe,EAAE,SAAS,EAAE;AAAA,MACpC,eAAe,IAAI,aAAa,EAAE,YAAY,EAAE,SAAS,EAAE;AAAA,IAC7D;AACA,QAAI,YAAY;AACd,cAAQ,MAAM,IAAI,UAAU,IAAI,SAAS,CAAC,CAAC;AAAA,IAC7C;AACA,UAAM,KAAK,QAAQ,KAAK,IAAI,CAAC;AAAA,EAC/B;AAEA,QAAM,KAAK,EAAE;AACb,QAAM,KAAK,SAAS;AAGpB,QAAM,KAAK,GAAG,EAAE,kBAAkB,QAAQ,IAAI,OAAO,WAAW,CAAC,MAAM;AACvE,QAAM,KAAK,GAAG,EAAE,mBAAmB,MAAM,IAAI,OAAO,YAAY,CAAC,MAAM;AACvE,QAAM,KAAK,GAAG,EAAE,gBAAgB,KAAK,IAAI,OAAO,SAAS,CAAC,MAAM;AAChE,QAAM,KAAK,EAAE;AACb,QAAM,KAAK,GAAG,EAAE,eAAe,KAAK,OAAO,SAAS,MAAM,EAAE;AAC5D,QAAM,KAAK,GAAG,EAAE,gBAAgB,KAAK,OAAO,WAAW,EAAE;AACzD,QAAM,KAAK,EAAE;AAGb,MAAI,OAAO,WAAW,OAAO,QAAQ,OAAO,SAAS,GAAG;AACtD,UAAM,KAAK,SAAS;AACpB,UAAM,KAAK,EAAE,aAAa;AAC1B,UAAM,KAAK,SAAS;AACpB,eAAW,KAAK,OAAO,QAAQ,QAAQ;AACrC,YAAM,QAAQ,EAAE,aACb,IAAI,CAAC,OAAO,gBAAgB,IAAI,CAAC,CAAC,EAClC,KAAK,IAAI;AACZ,YAAM,eAAe,EAAE,UAAU,KAAK,QAAQ,CAAC;AAC/C,YAAM;AAAA,QACJ,GAAG,KAAK,KAAK,IAAI,EAAE,WAAW,CAAC,wBAAmB,IAAI,EAAE,OAAO,CAAC,SAAS,WAAW;AAAA,MACtF;AAAA,IACF;AACA,UAAM,KAAK,GAAG,EAAE,eAAe,KAAK,IAAI,OAAO,QAAQ,YAAY,CAAC,MAAM;AAC1E,UAAM,KAAK,EAAE;AAAA,EACf;AAGA,MAAI,OAAO,SAAS;AAClB,UAAM,KAAK,SAAS;AACpB,UAAM,KAAK,EAAE,aAAa;AAC1B,UAAM,KAAK,SAAS;AACpB,UAAM,KAAK,gBAAgB,IAAI,OAAO,QAAQ,WAAW,CAAC,MAAM;AAChE,UAAM,KAAK,iBAAiB,IAAI,OAAO,QAAQ,YAAY,CAAC,MAAM;AAClE,QAAI,OAAO,QAAQ,sBAAsB,GAAG;AAC1C,YAAM;AAAA,QACJ,GAAG,EAAE,oBAAoB,CAAC,CAAC,KAAK,IAAI,OAAO,QAAQ,mBAAmB,CAAC;AAAA,MACzE;AAAA,IACF;AACA,QAAI,aAAa,GAAG,EAAE,aAAa,KAAK,IAAI,OAAO,QAAQ,SAAS,CAAC;AACrE,QAAI,OAAO,QAAQ,wBAAwB,GAAG;AAC5C,oBAAc,IAAI,EAAE,gBAAgB;AAAA,IACtC;AACA,UAAM,KAAK,UAAU;AACrB,UAAM,KAAK,EAAE;AAAA,EACf;AAGA,MAAI,YAAY;AACd,UAAM,KAAK,EAAE,gBAAgB;AAC7B,UAAM,KAAK,EAAE;AAAA,EACf;AAEA,MAAI,OAAO,eAAe;AACxB,UAAM,KAAK,oBAAoB,QAAQ,GAAG,uBAAuB,CAAC;AAAA,EACpE,OAAO;AACL,UAAM,KAAK,EAAE,UAAU;AAAA,EACzB;AAEA,SAAO,MAAM,KAAK,IAAI;AACxB;;;AP3NA,eAAsB,QACpB,YACA,OACA,GACA,QACA,QACiB;AACjB,QAAM,WAAW,WAAW,CAAC;AAC7B,MAAI,CAAC,UAAU;AACb,WAAO;AAAA,MACL;AAAA,IACF;AACA,WAAO;AAAA,EACT;AAEA,MAAI;AACJ,MAAI;AACF,UAAS,iBAAa,UAAU,MAAM;AAAA,EACxC,QAAQ;AACN,WAAO,MAAM,qBAAqB,QAAQ;AAAA,CAAI;AAC9C,WAAO;AAAA,EACT;AAEA,QAAM,SAAU,MAAM,QAAQ,KAAmB;AACjD,MAAI,WAAW,UAAU,WAAW,QAAQ;AAC1C,WAAO,MAAM,iCAAiC;AAC9C,WAAO;AAAA,EACT;AAGA,QAAM,cAAc,SAAS,QAAQ,WAAW,EAAE,IAAI;AACtD,MAAI;AACJ,MAAO,eAAW,WAAW,GAAG;AAC9B,QAAI;AACF,YAAM,aAAa,IAAI,WAAW,EAAE,aAAa,MAAM,CAAC;AACxD,uBAAiB,MAAM,WAAW,KAAK,WAAW;AAAA,IACpD,QAAQ;AACN,aAAO,MAAM,wBAAwB,WAAW;AAAA,CAAI;AACpD,aAAO;AAAA,IACT;AAAA,EACF;AAGA,QAAM,gBAAgB,MAAM,sBAAsB;AAClD,QAAM,kBACJ,kBAAkB,UAAa,kBAAkB;AACnD,QAAM,aACJ,OAAO,kBAAkB,WACrB,gBACA,SAAS,QAAQ,WAAW,EAAE,IAAI;AAExC,MAAI,mBAAmB,mBAAmB,QAAW;AACnD,WAAO,MAAM,EAAE,6BAA6B,IAAI;AAChD,WAAO;AAAA,EACT;AAGA,QAAM,QAAiB,MAAM,eAAe;AAC5C,QAAM,mBAA6B,MAAM,QAAQ,KAAK,IACjD,QACD,QACE,CAAC,KAAe,IAChB,CAAC;AAEP,QAAM,UAAU;AAChB,aAAW,MAAM,kBAAkB;AACjC,QAAI,CAAC,QAAQ,KAAK,EAAE,GAAG;AACrB,aAAO,MAAM,EAAE,4BAA4B,IAAI;AAC/C,aAAO;AAAA,IACT;AAAA,EACF;AAGA,QAAM,YACJ,QAAQ,IAAI,iBAAiB,KAAU,WAAQ,YAAQ,GAAG,SAAS;AACrE,QAAM,eAAoB;AAAA,IACxB;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACA,QAAM,aAAqC,CAAC;AAC5C,MAAO,eAAW,YAAY,GAAG;AAC/B,QAAI;AACF,YAAM,MAAM,KAAK,MAAS,iBAAa,cAAc,OAAO,CAAC;AAG7D,iBAAW,SAAS,IAAI,UAAU,CAAC,GAAG;AACpC,mBAAW,MAAM,IAAI,IAAI,MAAM;AAAA,MACjC;AAAA,IACF,QAAQ;AAAA,IAER;AAAA,EACF;AAGA,QAAM,WAAmC,EAAE,GAAG,WAAW;AACzD,aAAW,MAAM,kBAAkB;AACjC,UAAM,CAAC,SAAS,SAAS,IAAI,GAAG,MAAM,GAAG;AACzC,aAAS,SAAS,SAAS,EAAE,CAAC,IAAI,WAAW,SAAS;AAAA,EACxD;AACA,QAAM,eAA+B,OAAO,QAAQ,QAAQ,EAAE;AAAA,IAC5D,CAAC,CAAC,MAAM,MAAM,OAAO,EAAE,MAAM,SAAS,MAAM,EAAE,GAAG,OAAO;AAAA,EAC1D;AAEA,QAAM,SAAS,IAAI,aAAa;AAChC,MAAI;AACJ,MAAI;AACF,mBAAe,OAAO,MAAM,GAAG;AAAA,EACjC,SAAS,KAAK;AACZ,QAAI,eAAe,YAAY;AAC7B,UAAI,IAAI,SAAS,eAAe;AAC9B,eAAO,MAAM,EAAE,kBAAkB,IAAI;AAAA,MACvC,OAAO;AACL,eAAO,MAAM,EAAE,mBAAmB,IAAI,UAAW,IAAI,IAAI;AAAA,MAC3D;AACA,aAAO;AAAA,IACT;AACA,UAAM;AAAA,EACR;AAEA,QAAM,aAAa,IAAI,WAAW,cAAc,OAAO,UAAU;AAAA,IAC/D;AAAA,IACA,cAAc,aAAa,SAAS,IAAI,eAAe;AAAA,IACvD,YAAY,OAAO;AAAA,EACrB,CAAC;AACD,QAAM,SAAS,WAAW,eAAe,MAAM;AAC/C,QAAM,0BAA0B,aAAa,SAAS;AAEtD,MAAI,iBAAiB;AACnB,QAAI;AACF,YAAM,OAAO,cAAe,SAAS,UAAU;AAAA,IACjD,QAAQ;AACN,aAAO,MAAM,sCAAsC,UAAU;AAAA,CAAI;AACjE,aAAO;AAAA,IACT;AAAA,EACF;AAEA,MAAI,MAAM,MAAM,GAAG;AAEjB,UAAM,EAAE,eAAe,gBAAgB,GAAG,WAAW,IAAI;AACzD,WAAO,MAAM,KAAK,UAAU,YAAY,MAAM,CAAC,IAAI,IAAI;AAAA,EACzD,OAAO;AACL,WAAO,MAAM,aAAa,QAAQ,GAAG,uBAAuB,IAAI,IAAI;AAAA,EACtE;AAEA,MAAI,iBAAiB;AACnB,WAAO,MAAM,EAAE,gBAAgB,UAAU,IAAI,IAAI;AAAA,EACnD,WAAW,CAAC,OAAO,eAAe;AAChC,WAAO,MAAM,EAAE,6BAA6B,IAAI;AAAA,EAClD;AAEA,SAAO;AACT;;;AQvKA,YAAYC,SAAQ;AAKpB,eAAsB,YACpB,YACA,OACA,GACA,QACA,QACiB;AACjB,QAAM,WAAW,WAAW,CAAC;AAC7B,MAAI,CAAC,UAAU;AACb,WAAO,MAAM,4CAA4C;AACzD,WAAO;AAAA,EACT;AAEA,MAAI;AACJ,MAAI;AACF,UAAS,iBAAa,UAAU,MAAM;AAAA,EACxC,QAAQ;AACN,WAAO,MAAM,qBAAqB,QAAQ;AAAA,CAAI;AAC9C,WAAO;AAAA,EACT;AAEA,QAAM,SAAS,IAAI,aAAa;AAChC,MAAI;AACJ,MAAI;AACF,mBAAe,OAAO,MAAM,GAAG;AAAA,EACjC,SAAS,KAAK;AACZ,QAAI,eAAe,YAAY;AAC7B,UAAI,IAAI,SAAS,eAAe;AAC9B,eAAO,MAAM,EAAE,kBAAkB,IAAI;AAAA,MACvC,OAAO;AACL,eAAO,MAAM,EAAE,mBAAmB,IAAI,UAAW,IAAI,IAAI;AAAA,MAC3D;AACA,aAAO;AAAA,IACT;AACA,UAAM;AAAA,EACR;AAEA,SAAO,MAAM,EAAE,WAAW,aAAa,QAAQ,CAAC,IAAI,IAAI;AAExD,aAAW,SAAS,OAAO,gBAAgB;AACzC,QAAI;AACJ,YAAQ,MAAM,MAAM;AAAA,MAClB,KAAK;AACH,iBAAS,EAAE,gBAAgB,MAAM,GAAG;AACpC;AAAA,MACF,KAAK;AACH,iBAAS,EAAE,wBAAwB,MAAM,KAAK,MAAM,QAAQ;AAC5D;AAAA,MACF,KAAK;AACH,iBAAS,EAAE,cAAc,MAAM,KAAK,MAAM,UAAU,MAAM,IAAI;AAC9D;AAAA,MACF,KAAK;AACH,iBAAS,EAAE,iBAAiB,MAAM,GAAG;AACrC;AAAA,MACF,KAAK;AACH,iBAAS,EAAE,sBAAsB,MAAM,GAAG;AAC1C;AAAA,MACF,KAAK;AACH,iBAAS,EAAE,sBAAsB,MAAM,MAAM,MAAM,IAAI;AACvD;AAAA,IACJ;AACA,WAAO,MAAM,SAAS,IAAI;AAAA,EAC5B;AAEA,SAAO;AACT;;;ACtEA,YAAYC,SAAQ;AACpB,YAAYC,WAAU;AACtB,YAAYC,SAAQ;AACpB,YAAYC,YAAW;AAIhB,SAAS,kBAA0B;AACxC,QAAM,WAAW,QAAQ;AACzB,MAAI;AACJ,MAAI,aAAa,SAAS;AACxB,gBACE,QAAQ,IAAI,WAAgB,WAAQ,YAAQ,GAAG,WAAW,SAAS;AAAA,EACvE,OAAO;AACL,gBACE,QAAQ,IAAI,mBAAwB,WAAQ,YAAQ,GAAG,SAAS;AAAA,EACpE;AACA,SAAY,WAAK,WAAW,iBAAiB,gBAAgB;AAC/D;AAEA,SAAS,YAAY,UAInB;AACA,MAAI,QAAQ;AACZ,MAAI,MAAM;AACV,QAAM,aAAuB,CAAC;AAE9B,aAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,QAAQ,GAAG;AACnD,eAAW,KAAK,GAAG;AACnB,eAAW,KAAK,OAAO,KAAK,KAAK,GAAG;AAClC,UAAI,IAAI,MAAO,SAAQ;AACvB,UAAI,IAAI,IAAK,OAAM;AAAA,IACrB;AAAA,EACF;AACA,SAAO,EAAE,OAAO,KAAK,YAAY,WAAW,KAAK,EAAE,KAAK,IAAI,EAAE;AAChE;AAEA,eAAsB,aACpB,UACiC;AACjC,SAAO,IAAI,QAAQ,CAACC,UAAS,WAAW;AACtC,UAAM,MAAM,qDAAqD,QAAQ;AACzE,IACG,WAAI,KAAK,CAAC,QAAQ;AACjB,UAAI,OAAO;AACX,UAAI,GAAG,QAAQ,CAAC,UAAW,QAAQ,KAAM;AACzC,UAAI,GAAG,OAAO,MAAM;AAClB,cAAM,QAAgC,CAAC;AACvC,cAAM,QAAQ,KAAK,MAAM,IAAI;AAI7B,mBAAW,QAAQ,OAAO;AACxB,gBAAM,QAAQ,KAAK,MAAM,GAAG;AAC5B,cAAI,MAAM,SAAS,EAAG;AACtB,gBAAM,OAAO,MAAM,CAAC,EAAE,KAAK;AAC3B,gBAAM,OAAO,WAAW,MAAM,CAAC,EAAE,KAAK,CAAC;AACvC,cAAI,sBAAsB,KAAK,IAAI,KAAK,CAAC,MAAM,IAAI,KAAK,OAAO,GAAG;AAChE,kBAAM,IAAI,IAAI;AAAA,UAChB;AAAA,QACF;AACA,QAAAA,SAAQ,KAAK;AAAA,MACf,CAAC;AACD,UAAI,GAAG,SAAS,MAAM;AAAA,IACxB,CAAC,EACA,GAAG,SAAS,MAAM;AAAA,EACvB,CAAC;AACH;AAEA,eAAsB,YACpB,cACA,QACA,QACA,GACe;AACf,SAAO,MAAM,EAAE,sBAAsB,IAAI;AAEzC,MAAI,WAA0B,CAAC;AAC/B,MAAI;AACF,eAAW,KAAK;AAAA,MACX,iBAAa,cAAc,MAAM;AAAA,IACtC;AAAA,EACF,QAAQ;AAAA,EAER;AAEA,MAAI,aAAa;AACjB,aAAW,YAAY,CAAC,OAAO,OAAO,KAAK,GAAG;AAC5C,QAAI;AACF,YAAM,WAAW,MAAM,aAAa,QAAQ;AAC5C,YAAM,eAAe,SAAS,QAAQ,KAAK,CAAC;AAC5C,UAAI,QAAQ;AACZ,iBAAW,CAAC,MAAM,IAAI,KAAK,OAAO,QAAQ,QAAQ,GAAG;AACnD,YAAI,CAAC,aAAa,IAAI,EAAG;AACzB,qBAAa,IAAI,IAAI;AAAA,MACvB;AACA,eAAS,QAAQ,IAAI;AACrB,oBAAc;AAAA,IAChB,QAAQ;AACN,aAAO,MAAM,mBAAmB,QAAQ;AAAA,CAAU;AAAA,IACpD;AAAA,EACF;AAEA,EAAG,cAAe,cAAQ,YAAY,GAAG,EAAE,WAAW,KAAK,CAAC;AAC5D,EAAG;AAAA,IACD;AAAA,IACA,KAAK,UAAU,UAAU,MAAM,CAAC,IAAI;AAAA,IACpC;AAAA,EACF;AACA,SAAO,MAAM,EAAE,gBAAgB,UAAU,IAAI,IAAI;AACjD,SAAO,MAAM,EAAE,qBAAqB,YAAY,IAAI,IAAI;AAC1D;AAEA,eAAsB,SACpB,YACA,OACA,GACA,QACA,QACiB;AACjB,MAAI,MAAM,OAAO,GAAG;AAClB,UAAM,WAAW,kBAAkB;AACnC,UAAM,EAAE,OAAO,KAAK,WAAW,IAAI,YAAY,QAAQ;AACvD,WAAO,MAAM,EAAE,cAAc,OAAO,KAAK,UAAU,IAAI,IAAI;AAC3D,WAAO,MAAM,EAAE,gBAAgB,IAAI;AACnC,WAAO;AAAA,EACT;AAEA,MAAI,MAAM,QAAQ,GAAG;AACnB,UAAM,YAAY,gBAAgB,GAAG,QAAQ,QAAQ,CAAC;AACtD,WAAO;AAAA,EACT;AAEA,SAAO,MAAM,iDAAiD;AAC9D,SAAO;AACT;;;ACtIA,IAAMC,qBAAuC,CAAC,MAAM,IAAI;AAExD,eAAsB,UACpB,YACA,OACA,GACA,QACA,QACiB;AACjB,MAAI,MAAM,MAAM,MAAM,QAAW;AAC/B,UAAM,OAAO,MAAM,MAAM;AACzB,QAAI,CAACA,mBAAkB,SAAS,IAAuB,GAAG;AACxD,aAAO,MAAM,uBAAuB,IAAI;AAAA,CAAkB;AAC1D,aAAO;AAAA,IACT;AACA,eAAW,IAAuB;AAClC,WAAO,MAAM,EAAE,cAAc,IAAuB,IAAI,IAAI;AAC5D,WAAO;AAAA,EACT;AAEA,MAAI,MAAM,MAAM,GAAG;AACjB,UAAM,SAAS,cAAc;AAC7B,WAAO,MAAM,EAAE,kBAAkB,MAAM,IAAI,IAAI;AAC/C,WAAO;AAAA,EACT;AAEA,SAAO,MAAM,uDAAuD;AACpE,SAAO;AACT;;;AC1BA,YAAYC,SAAQ;AACpB,YAAYC,WAAU;AACtB,YAAYC,SAAQ;AACpB,SAAS,iBAAAC,sBAAqB;AAC9B,SAAS,aAAAC,kBAAiB;;;AC8C1B,SAAS,WAAW,SAAyB;AAC3C,QAAM,CAAC,MAAM,IAAI,EAAE,IAAI,QAAQ,MAAM,GAAG;AACxC,SAAO,GAAG,EAAE,IAAI,EAAE,IAAI,IAAI;AAC5B;AAYO,SAAS,YAAY,UAAoC;AAE9D,MAAI,SAAS,YAAY,eAAe;AAEtC,WAAO;AAAA,EACT;AAGA,QAAM,aAAa;AAAA,IACjB;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAGA,MAAI,UAAU;AACd,MAAI,SAAS,YAAY,oBAAoB;AAC3C,cAAU,QAAQ,OAAO,CAAC,MAAM,MAAM,MAAM;AAAA,EAC9C,WAAW,SAAS,YAAY,wBAAwB;AACtD,cAAU,QAAQ,OAAO,CAAC,MAAM,MAAM,UAAU;AAAA,EAClD,WAAW,SAAS,YAAY,qBAAqB;AACnD,cAAU,QAAQ,OAAO,CAAC,MAAM,MAAM,OAAO;AAAA,EAC/C;AAGA,QAAM,WAAW,CACf,QACA,mBACW;AACX,WAAO,eAAe,IAAI,CAAC,QAAQ,OAAO,GAAG,KAAK,EAAE,EAAE,KAAK,GAAG;AAAA,EAChE;AAGA,QAAM,SAAS,QAAQ,KAAK,GAAG;AAC/B,QAAM,OAAO,CAAC,MAAM;AAGpB,WAAS,WAAW,GAAG,WAAW,SAAS,aAAa,QAAQ,YAAY;AAC1E,UAAM,KAAK,SAAS,aAAa,QAAQ;AAKzC,UAAM,aAAa,CAAC,GAAG,MAAM,GAAG;AAChC,UAAM,QAAQ,aAAa,GAAG;AAE9B,UAAM,YAAoC;AAAA,MACxC,MAAM,WAAW,GAAG,IAAI;AAAA,MACxB,MAAM;AAAA,MACN,SAAS,GAAG;AAAA,MACZ,MAAM,GAAG;AAAA,MACT,UAAU;AAAA,MACV,oBAAoB;AAAA,MACpB,UAAU,OAAO,GAAG,GAAG;AAAA,MACvB,OAAO,GAAG,MAAM,QAAQ,CAAC;AAAA,MACzB,eAAe,WAAW,QAAQ,CAAC;AAAA,MACnC,wBAAwB,GAAG;AAAA,MAC3B,OAAO,WAAW,QAAQ,CAAC;AAAA,MAC3B,kBAAkB;AAAA,MAClB,iBAAiB;AAAA,MACjB,sBAAsB,CAAC,KAAK,IAAI,GAAG,GAAG,GAAG,QAAQ,CAAC;AAAA,MAClD,8BAA8B;AAAA,MAC9B,OAAO,MAAM,QAAQ,CAAC;AAAA,MACtB,kBAAkB;AAAA,MAClB,YAAY,UAAU,SAAS,EAAE,IAAI,QAAQ;AAAA,IAC/C;AAEA,UAAM,MAAM,SAAS,WAAW,OAAO;AACvC,SAAK,KAAK,GAAG;AAAA,EACf;AAEA,SAAO,KAAK,KAAK,IAAI;AACvB;;;ACxJA,SAAS,iBAAiB;AAC1B,SAAS,iBAAAC,sBAAqB;AAC9B,SAAS,QAAAC,aAAY;AAoCrB,SAAS,cAAc,MAAsB;AAC3C,MAAI,CAAC,KAAM,QAAO;AAClB,QAAM,QAAQ,KAAK,MAAM,sCAAsC;AAC/D,SAAO,QAAQ,SAAS,MAAM,CAAC,GAAG,EAAE,IAAI;AAC1C;AAKA,SAAS,YAAY,SAA0B;AAC7C,MAAI;AACF,UAAM,MAAM,KAAK,MAAM,OAAO;AAC9B,WACE,OAAO,QAAQ,YACf,QAAQ,QACR,iBAAiB,OACjB,kBAAkB,OAClB,UAAU;AAAA,EAEd,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAKA,SAAS,WACP,QACA,MACA,UACA,kBACA,kBAKe;AAEf,QAAM,WAAW,OAAO,SAAS,KAAK;AACtC,MAAI;AAEJ,MAAI,UAAU;AAEZ,aAAS,UAAU,QAAQ,CAAC,QAAQ,GAAG,IAAI,GAAG;AAAA,MAC5C,UAAU;AAAA,MACV,SAAS;AAAA,MACT,KAAK,EAAE,GAAG,QAAQ,KAAK,oBAAoB,OAAU;AAAA,IACvD,CAAC;AAAA,EACH,OAAO;AAEL,aAAS,UAAU,QAAQ,MAAM;AAAA,MAC/B,UAAU;AAAA,MACV,SAAS;AAAA,MACT,KAAK,EAAE,GAAG,QAAQ,KAAK,oBAAoB,OAAU;AAAA,IACvD,CAAC;AAAA,EACH;AAEA,QAAM,WAAW,OAAO,UAAU;AAClC,QAAM,SAAS,OAAO,UAAU;AAChC,QAAM,SAAS,OAAO,UAAU;AAGhC,QAAM,gBAAgB,aAAa;AAGnC,QAAM,aAAa,iBAAiB,QAAQ,QAAQ,QAAQ;AAE5D,MAAI,OAAO,iBAAiB,WAAW;AACvC,MAAI;AAEJ,MAAI,CAAC,eAAe;AAClB,cAAU,gCAAgC,gBAAgB,SAAS,QAAQ;AAAA,EAC7E,WAAW,CAAC,WAAW,OAAO;AAC5B,cAAU,WAAW;AAAA,EACvB;AAEA,SAAO;AAAA,IACL,KAAK;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;AAKO,SAAS,YACd,UACA,YACA,WACA,QACgB;AAEhB,QAAM,cAAc,GAAG,SAAS,EAAE,IAAI,SAAS,IAAI;AACnD,QAAM,UAAUA,MAAK,WAAW,WAAW;AAC3C,EAAAD,eAAc,SAAS,YAAY,MAAM;AAEzC,QAAM,UAA2B,CAAC;AAGlC,UAAQ;AAAA,IACN;AAAA,MACE;AAAA,MACA,CAAC,QAAQ,OAAO;AAAA,MAChB;AAAA,MACA,SAAS,OAAO;AAAA,MAChB,CAAC,WAAW;AACV,YAAI,SAAS,OAAO,cAAc,GAAG;AACnC,gBAAM,UAAU,0BAA0B,KAAK,MAAM;AACrD,iBAAO,UACH,EAAE,OAAO,KAAK,IACd;AAAA,YACE,OAAO;AAAA,YACP,QAAQ;AAAA,UACV;AAAA,QACN;AACA,eAAO,EAAE,OAAO,KAAK;AAAA,MACvB;AAAA,IACF;AAAA,EACF;AAGA,UAAQ;AAAA,IACN;AAAA,MACE;AAAA,MACA,CAAC,QAAQ,YAAY,QAAQ,OAAO;AAAA,MACpC;AAAA,MACA,SAAS,OAAO;AAAA,MAChB,CAAC,WAAW;AACV,YAAI,SAAS,OAAO,cAAc,GAAG;AACnC,gBAAM,UAAU,0BAA0B,KAAK,MAAM;AACrD,iBAAO,UACH,EAAE,OAAO,KAAK,IACd;AAAA,YACE,OAAO;AAAA,YACP,QACE;AAAA,UACJ;AAAA,QACN;AACA,eAAO,EAAE,OAAO,KAAK;AAAA,MACvB;AAAA,IACF;AAAA,EACF;AAGA,UAAQ;AAAA,IACN;AAAA,MACE;AAAA,MACA,CAAC,QAAQ,UAAU,OAAO;AAAA,MAC1B;AAAA,MACA,SAAS,OAAO;AAAA,MAChB,CAAC,WAAW;AACV,YAAI,SAAS,OAAO,cAAc,GAAG;AACnC,gBAAM,UAAU,YAAY,MAAM;AAClC,iBAAO,UACH,EAAE,OAAO,KAAK,IACd;AAAA,YACE,OAAO;AAAA,YACP,QACE;AAAA,UACJ;AAAA,QACN;AACA,eAAO,EAAE,OAAO,KAAK;AAAA,MACvB;AAAA,IACF;AAAA,EACF;AAGA,UAAQ;AAAA,IACN;AAAA,MACE;AAAA,MACA,CAAC,QAAQ,UAAU,MAAM,OAAO;AAAA,MAChC;AAAA,MACA,SAAS,OAAO;AAAA,MAChB,CAAC,WAAW;AACV,YAAI,SAAS,OAAO,YAAY,GAAG;AACjC,gBAAM,WAAW,OAAO,KAAK,EAAE,SAAS;AACxC,iBAAO,WACH,EAAE,OAAO,KAAK,IACd;AAAA,YACE,OAAO;AAAA,YACP,QAAQ;AAAA,UACV;AAAA,QACN;AACA,eAAO,EAAE,OAAO,KAAK;AAAA,MACvB;AAAA,IACF;AAAA,EACF;AAGA,UAAQ;AAAA,IACN;AAAA,MACE;AAAA,MACA,CAAC,YAAY,OAAO;AAAA,MACpB;AAAA,MACA,SAAS,OAAO;AAAA,MAChB,CAAC,WAAW;AACV,YAAI,SAAS,OAAO,kBAAkB,EAAG,QAAO,EAAE,OAAO,KAAK;AAC9D,cAAM,WAAW,OAAO,KAAK,EAAE,SAAS;AACxC,eAAO,WACH,EAAE,OAAO,KAAK,IACd;AAAA,UACE,OAAO;AAAA,UACP,QAAQ;AAAA,QACV;AAAA,MACN;AAAA,IACF;AAAA,EACF;AAIA,QAAM,eAAe,QAAQ,CAAC;AAC9B,QAAM,qBAAqB,cAAc,aAAa,MAAM;AAC5D,QAAM,mBAAmB,sBAAsB,SAAS,OAAO;AAG/D,QAAM,OAAO,QAAQ,MAAM,CAAC,MAAM,EAAE,IAAI,KAAK;AAE7C,SAAO;AAAA,IACL,IAAI,SAAS;AAAA,IACb,UAAU,SAAS;AAAA,IACnB,MAAM,SAAS;AAAA,IACf,aAAa,SAAS;AAAA,IACtB;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,sBAAsB,SAAS,OAAO;AAAA,IACtC;AAAA,EACF;AACF;;;AC/PO,SAAS,YACd,SACA,YACA,UACc;AACd,QAAM,SAAS,QAAQ,OAAO,CAAC,MAAM,EAAE,IAAI,EAAE;AAC7C,QAAM,SAAS,QAAQ,SAAS;AAEhC,SAAO;AAAA,IACL,gBAAgB,QAAQ;AAAA,IACxB;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;AAKO,SAAS,WAAW,QAA8B;AACvD,SAAO,KAAK,UAAU,QAAQ,MAAM,CAAC;AACvC;AAKO,SAAS,YAAY,QAA8B;AACxD,QAAM,QAAkB,CAAC;AAGzB,QAAM,KAAK,kCAA6B;AACxC,QAAM;AAAA,IACJ,YAAY,OAAO,cAAc,iBAAiB,OAAO,MAAM,iBAAiB,OAAO,MAAM;AAAA,EAC/F;AACA,QAAM,KAAK,EAAE;AAGb,QAAM,QAAQ;AACd,QAAM,cAAc;AACpB,QAAM,UAAU;AAChB,QAAM,YAAY;AAElB,QAAM,KAAK,UAAU,OAAO,aAAa,SAAS,SAAS,CAAC;AAG5D,aAAW,UAAU,OAAO,SAAS;AACnC,UAAM,QAAQ,OAAO,GAAG,SAAS,GAAG,GAAG;AACvC,UAAM,SAAS,OAAO,OAAO,gBAAW;AAExC,UAAM,KAAK,UAAU,OAAO,OAAO,UAAU,OAAO,aAAa,MAAM,CAAC;AAGxE,QAAI,CAAC,OAAO,MAAM;AAChB,iBAAW,aAAa,OAAO,SAAS;AACtC,YAAI,CAAC,UAAU,MAAM;AACnB,cAAI,UAAU,SAAS;AACrB,gBAAI,cAAc;AAGlB,gBAAI,UAAU,QAAQ,SAAS,oBAAoB,GAAG;AACpD,oBAAM,QAAQ,UAAU,QAAQ;AAAA,gBAC9B;AAAA,cACF;AACA,kBAAI,OAAO;AACT,8BAAc,eAAe,MAAM,CAAC,CAAC,mBAAmB,MAAM,CAAC,CAAC;AAAA,cAClE;AAAA,YACF,OAAO;AAEL,4BAAc,kCAA6B,UAAU,OAAO;AAAA,YAC9D;AAEA,kBAAM,KAAK,aAAQ,UAAU,GAAG,KAAK,WAAW,EAAE;AAAA,UACpD;AAAA,QACF;AAAA,MACF;AAGA,UAAI,CAAC,OAAO,kBAAkB;AAC5B,cAAM;AAAA,UACJ,sCAA4B,OAAO,oBAAoB,aAAa,OAAO,kBAAkB;AAAA,QAC/F;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAGA,QAAM,KAAK,EAAE;AACb,QAAM,KAAK,YAAY,OAAO,MAAM,EAAE;AACtC,QAAM,KAAK,YAAY,OAAO,MAAM,EAAE;AACtC,QAAM,KAAK,EAAE;AAEb,SAAO,MAAM,KAAK,IAAI;AACxB;AAKA,SAAS,UACP,IACA,UACA,aACA,OACQ;AAER,QAAM,UAAU;AAChB,QAAM,gBAAgB;AACtB,QAAM,YAAY;AAClB,QAAM,aAAa;AAGnB,QAAM,QAAQ,GAAG,OAAO,OAAO;AAC/B,QAAM,cAAc,cAAc,UAAU,aAAa;AACzD,QAAM,UAAU,cAAc,aAAa,SAAS;AACpD,QAAM,WAAW,MAAM,OAAO,UAAU;AAExC,SAAO,GAAG,KAAK,KAAK,WAAW,KAAK,OAAO,KAAK,QAAQ;AAC1D;AAKA,SAAS,cAAc,KAAa,UAA0B;AAC5D,MAAI,IAAI,SAAS,UAAU;AACzB,WAAO,IAAI,MAAM,GAAG,WAAW,CAAC,IAAI;AAAA,EACtC;AACA,SAAO,IAAI,OAAO,QAAQ;AAC5B;;;AHvIA,eAAsB,cACpB,cACA,OACA,QACA,QACiB;AAEjB,QAAM,WAAY,MAAM,OAAO,KAA4B;AAC3D,QAAM,aAAa,SAAS,MAAM,GAAG;AACrC,MAAI,WAAW,WAAW,GAAG;AAC3B,WAAO;AAAA,MACL,8DAA8D,QAAQ;AAAA;AAAA,IACxE;AACA,WAAO;AAAA,EACT;AACA,QAAM,aAAa,SAAS,WAAW,CAAC,GAAG,EAAE;AAC7C,QAAM,WAAW,SAAS,WAAW,CAAC,GAAG,EAAE;AAC3C,MACE,MAAM,UAAU,KAChB,MAAM,QAAQ,KACd,cAAc,KACd,YAAY,KACZ,aAAa,YACb,aAAa,OACb,WAAW,KACX;AACA,WAAO;AAAA,MACL,kGAA6F,QAAQ;AAAA;AAAA,IACvG;AACA,WAAO;AAAA,EACT;AAGA,QAAM,YAAiB,cAAQE,eAAc,YAAY,GAAG,CAAC;AAC7D,QAAM,eAAoB,WAAK,WAAW,8BAA8B;AAGxE,MAAI;AACJ,MAAI;AACF,UAAM,MAAS,iBAAa,cAAc,MAAM;AAChD,eAAW,KAAK,MAAM,GAAG;AAAA,EAC3B,QAAQ;AACN,WAAO,MAAM,4CAA4C,YAAY;AAAA,CAAI;AACzE,WAAO;AAAA,EACT;AAGA,QAAM,SAAS,QAAQ,KAAK,CAAC;AAG7B,MAAI;AACJ,QAAM,gBAAgB,MAAM,YAAY;AACxC,MAAI,eAAe;AACjB,gBAAY;AACZ,IAAG,cAAU,WAAW,EAAE,WAAW,KAAK,CAAC;AAAA,EAC7C,OAAO;AACL,gBAAe,gBAAiB,WAAQ,WAAO,GAAG,uBAAuB,CAAC;AAAA,EAC5E;AAGA,QAAM,aAAaC,WAAU,QAAQ,CAAC,QAAQ,SAAS,SAAS,GAAG;AAAA,IACjE,UAAU;AAAA,IACV,SAAS;AAAA,EACX,CAAC;AACD,OAAK,WAAW,UAAU,OAAO,GAAG;AAClC,WAAO,MAAM,0DAA0D;AAAA,EACzE;AAEA,QAAM,aAAaA,WAAU,QAAQ,CAAC,QAAQ,UAAU,QAAQ,GAAG;AAAA,IACjE,UAAU;AAAA,IACV,SAAS;AAAA,EACX,CAAC;AACD,OAAK,WAAW,UAAU,OAAO,GAAG;AAClC,WAAO,MAAM,kCAAkC;AAAA,EACjD;AAGA,QAAM,YAAY,SAAS,UAAU,OAAO,CAAC,MAAM;AACjD,UAAM,KAAK,SAAS,EAAE,IAAI,EAAE;AAC5B,WAAO,MAAM,cAAc,MAAM;AAAA,EACnC,CAAC;AAGD,QAAM,UAAU,CAAC;AACjB,aAAW,YAAY,WAAW;AAChC,UAAM,MAAM,YAAY,QAAQ;AAChC,UAAM,SAAS,YAAY,UAAU,KAAK,WAAW,MAAM;AAC3D,YAAQ,KAAK,MAAM;AACnB,WAAO;AAAA,MACL,MAAM,OAAO,OAAO,WAAM,QAAG,KAAK,SAAS,EAAE,IAAI,SAAS,IAAI;AAAA;AAAA,IAChE;AAAA,EACF;AAGA,QAAM,SAAS,YAAY,SAAS,YAAY,QAAQ;AACxD,MAAI,MAAM,MAAM,GAAG;AACjB,WAAO,MAAM,WAAW,MAAM,IAAI,IAAI;AAAA,EACxC,OAAO;AACL,WAAO,MAAM,YAAY,MAAM,IAAI,IAAI;AAAA,EACzC;AAGA,MAAI,CAAC,MAAM,MAAM,GAAG;AAClB,IAAG,WAAO,WAAW,EAAE,WAAW,MAAM,OAAO,KAAK,CAAC;AAAA,EACvD;AAGA,SAAO,OAAO,WAAW,IAAI,IAAI;AACnC;;;AI3HA,YAAYC,SAAQ;AACpB,YAAYC,WAAU;AAOtB,eAAsB,YACpB,YACA,OACA,GACA,QACA,QACiB;AACjB,QAAM,UAAU,QAAQ,MAAM,SAAS,CAAC;AAGxC,MAAI,CAAC,QAAQ,MAAM,SAAS,CAAC,SAAS;AACpC,WAAO,MAAM,EAAE,sBAAsB,IAAI;AACzC,WAAO;AAAA,EACT;AAEA,QAAM,UAAU,WAAW,CAAC;AAC5B,MAAI,CAAC,SAAS;AACZ,WAAO,MAAM,wDAAwD;AACrE,WAAO;AAAA,EACT;AAEA,MAAI;AACJ,MAAI;AACF,UAAS,iBAAa,SAAS,MAAM;AAAA,EACvC,QAAQ;AACN,WAAO,MAAM,qBAAqB,OAAO;AAAA,CAAI;AAC7C,WAAO;AAAA,EACT;AAEA,QAAM,SAAS,IAAI,aAAa;AAChC,MAAI;AACJ,MAAI;AACF,mBAAe,OAAO,MAAM,GAAG;AAAA,EACjC,SAAS,KAAK;AACZ,QAAI,eAAe,YAAY;AAC7B,UAAI,IAAI,SAAS,eAAe;AAC9B,eAAO,MAAM,EAAE,kBAAkB,IAAI;AAAA,MACvC,OAAO;AACL,eAAO,MAAM,EAAE,mBAAmB,IAAI,UAAW,IAAI,IAAI;AAAA,MAC3D;AACA,aAAO;AAAA,IACT;AACA,UAAM;AAAA,EACR;AAGA,QAAM,OAAO,QAAQ,QAAQ,WAAW,EAAE;AAC1C,QAAM,cAAc,OAAO;AAE3B,MAAI,SAAS;AAEX,WAAO,MAAM,EAAE,yBAAyB,IAAI;AAE5C,UAAM,gBAAgB,oBAAI,IAAoB;AAC9C,eAAW,MAAM,cAAc;AAC7B,UAAI,GAAG,QAAQ,CAAC,cAAc,IAAI,GAAG,IAAI,GAAG;AAC1C,sBAAc,IAAI,GAAG,MAAM,GAAG,OAAO;AAAA,MACvC;AAAA,IACF;AAEA,UAAM,kBAAqC,CAAC;AAC5C,eAAW,CAAC,MAAM,OAAO,KAAK,cAAc,QAAQ,GAAG;AACrD,sBAAgB,IAAI,IAAI;AAAA,QACtB;AAAA,QACA,YAAY;AAAA,QACZ,YAAY;AAAA,QACZ,YAAY;AAAA,QACZ,SAAS;AAAA,QACT,aAAa;AAAA,QACb,iBAAiB;AAAA,QACjB,QAAQ;AAAA,MACV;AAAA,IACF;AAEA,UAAM,iBAAiB,KAAK;AAAA,MAC1B;AAAA,QACE,SAAS;AAAA,QACT,cAAa,oBAAI,KAAK,GAAE,YAAY;AAAA,QACpC;AAAA,QACA,UAAU,CAAC;AAAA,MACb;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAEA,QAAI;AACF,MAAG,kBAAc,aAAa,gBAAgB,OAAO;AAAA,IACvD,QAAQ;AACN,aAAO,MAAM,yBAAyB,WAAW;AAAA,CAAI;AACrD,aAAO;AAAA,IACT;AAEA,WAAO,MAAM,EAAE,gBAAgB,WAAW,IAAI,IAAI;AAClD,WAAO;AAAA,EACT;AAGA,QAAM,aAAa,IAAI,WAAW,EAAE,aAAa,KAAK,CAAC;AAEvD,MAAO,eAAW,WAAW,GAAG;AAC9B,UAAM,cAAc,MAAM,WAAW,KAAK,WAAW;AACrD,UAAM,iBAAiB,OAAO,OAAO,WAAW,EAAE;AAAA,MAChD,CAAC,MAAM,EAAE;AAAA,IACX,EAAE;AACF,WAAO,MAAM,EAAE,oBAAoB,cAAc,CAAC;AAElD,UAAM,SAAS,MAAM,SAAS,QAAQ,KAAK;AAC3C,QAAI,OAAO,KAAK,EAAE,YAAY,MAAM,KAAK;AACvC,aAAO;AAAA,IACT;AAAA,EACF;AAEA,QAAM,WAAW,SAAS,cAAc,WAAW;AACnD,SAAO,MAAM,EAAE,gBAAqB,cAAQ,WAAW,CAAC,IAAI,IAAI;AAChE,SAAO;AACT;AAEA,SAAS,SAAS,QAAgD;AAChE,SAAO,IAAI,QAAQ,CAACC,aAAY;AAC9B,QAAI,MAAM;AACV,UAAM,SAAS,CAAC,UAA2B;AACzC,aAAO,MAAM,SAAS;AACtB,YAAM,KAAK,IAAI,QAAQ,IAAI;AAC3B,UAAI,OAAO,IAAI;AACb,eAAO,eAAe,QAAQ,MAAM;AACpC,QAAAA,SAAQ,IAAI,MAAM,GAAG,EAAE,CAAC;AAAA,MAC1B;AAAA,IACF;AACA,WAAO,GAAG,QAAQ,MAAM;AAAA,EAC1B,CAAC;AACH;;;ArB/HA,eAAe,OAAsB;AACnC,QAAM,EAAE,QAAQ,YAAY,IAAI,UAAU;AAAA,IACxC,MAAM,QAAQ,KAAK,MAAM,CAAC;AAAA,IAC1B,SAAS;AAAA,MACP,MAAM,EAAE,MAAM,SAAS;AAAA,MACvB,QAAQ,EAAE,MAAM,SAAS;AAAA,MACzB,MAAM,EAAE,MAAM,SAAS;AAAA,MACvB,MAAM,EAAE,MAAM,WAAW,SAAS,MAAM;AAAA,MACxC,OAAO,EAAE,MAAM,WAAW,SAAS,MAAM;AAAA,MACzC,QAAQ,EAAE,MAAM,WAAW,SAAS,MAAM;AAAA,MAC1C,MAAM,EAAE,MAAM,WAAW,SAAS,MAAM;AAAA,MACxC,OAAO,EAAE,MAAM,SAAS;AAAA,MACxB,MAAM,EAAE,MAAM,WAAW,SAAS,MAAM;AAAA,MACxC,cAAc,EAAE,MAAM,SAAS;AAAA,MAC/B,SAAS,EAAE,MAAM,WAAW,SAAS,MAAM;AAAA,IAC7C;AAAA,IACA,kBAAkB;AAAA,IAClB,QAAQ;AAAA,EACV,CAAC;AAGD,QAAM,SAAS,cAAc,OAAO,IAA0B;AAC9D,QAAM,IAAI,WAAW,MAAM;AAE3B,QAAM,UAAU,YAAY,CAAC;AAC7B,QAAM,kBAAkB,YAAY,MAAM,CAAC;AAC3C,QAAM,QAAQ;AAEd,QAAM,SAAS,QAAQ;AACvB,QAAM,SAAS,QAAQ;AAEvB,MAAI,WAAW;AAEf,MAAI;AACF,YAAQ,SAAS;AAAA,MACf,KAAK;AACH,mBAAW,MAAM,QAAQ,iBAAiB,OAAO,GAAG,QAAQ,MAAM;AAClE;AAAA,MACF,KAAK;AACH,mBAAW,MAAM,YAAY,iBAAiB,OAAO,GAAG,QAAQ,MAAM;AACtE;AAAA,MACF,KAAK;AACH,mBAAW,MAAM,SAAS,iBAAiB,OAAO,GAAG,QAAQ,MAAM;AACnE;AAAA,MACF,KAAK;AACH,mBAAW,MAAM,UAAU,iBAAiB,OAAO,GAAG,QAAQ,MAAM;AACpE;AAAA,MACF,KAAK;AACH,mBAAW,MAAM,cAAc,iBAAiB,OAAO,QAAQ,MAAM;AACrE;AAAA,MACF,KAAK;AACH,mBAAW,MAAM,YAAY,iBAAiB,OAAO,GAAG,QAAQ,MAAM;AACtE;AAAA,MACF;AACE,eAAO;AAAA,UACL;AAAA,QACF;AACA,mBAAW;AAAA,IACf;AAAA,EACF,SAAS,KAAK;AACZ,QAAI,eAAe,qBAAqB;AACtC,aAAO,MAAM,IAAI,UAAU,IAAI;AAC/B,iBAAW;AAAA,IACb,WAAW,eAAe,YAAY;AACpC,UAAI,IAAI,SAAS,eAAe;AAC9B,eAAO,MAAM,EAAE,kBAAkB,IAAI;AAAA,MACvC,OAAO;AACL,eAAO,MAAM,EAAE,mBAAmB,IAAI,UAAW,IAAI,IAAI;AAAA,MAC3D;AACA,iBAAW;AAAA,IACb,WAAW,eAAe,kBAAkB;AAC1C,aAAO,MAAM,EAAE,gBAAgB,IAAI,MAAM,IAAI,IAAI,IAAI,IAAI;AACzD,iBAAW;AAAA,IACb,OAAO;AACL,YAAM;AAAA,IACR;AAAA,EACF;AAEA,UAAQ,KAAK,QAAQ;AACvB;AAEA,KAAK,EAAE,MAAM,CAAC,QAAQ;AACpB,UAAQ,OAAO,MAAM,oBAAoB,OAAO,GAAG,CAAC;AAAA,CAAI;AACxD,UAAQ,KAAK,CAAC;AAChB,CAAC;","names":["path","path","fs","path","os","path","fs","get","isoDate","path","roundHalfUp","fs","resolve","fs","fs","path","os","https","resolve","SUPPORTED_LOCALES","fs","path","os","fileURLToPath","spawnSync","writeFileSync","join","fileURLToPath","spawnSync","fs","path","resolve"]}
1
+ {"version":3,"sources":["../../src/cli/index.ts","../../src/i18n/it.ts","../../src/i18n/en.ts","../../src/i18n/settings.ts","../../src/i18n/index.ts","../../src/errors.ts","../../src/cli/commands/calc.ts","../../src/rates/index.ts","../../src/parser/warnings.ts","../../src/parser/index.ts","../../src/dichiarazione/engine.ts","../../src/calculator/index.ts","../../src/classifier/index.ts","../../src/cli/renderer.ts","../../src/cli/commands/validate.ts","../../src/cli/commands/rates.ts","../../src/cli/commands/config.ts","../../src/cli/commands/stress-test.ts","../../src/stress/generator.ts","../../src/stress/runner.ts","../../src/stress/reporter.ts","../../src/cli/commands/classify.ts"],"sourcesContent":["import { parseArgs } from \"node:util\";\nimport { resolveLocale, getStrings } from \"../i18n/index.js\";\nimport { ParseError, CalculationError } from \"../errors.js\";\nimport { runCalc } from \"./commands/calc.js\";\nimport { runValidate } from \"./commands/validate.js\";\nimport { runRates } from \"./commands/rates.js\";\nimport { runConfig } from \"./commands/config.js\";\nimport { runStressTest } from \"./commands/stress-test.js\";\nimport { runClassify } from \"./commands/classify.js\";\nimport { ClassificationError } from \"../errors.js\";\n\nasync function main(): Promise<void> {\n const { values, positionals } = parseArgs({\n args: process.argv.slice(2),\n options: {\n lang: { type: \"string\" },\n method: { type: \"string\" },\n year: { type: \"string\" },\n json: { type: \"boolean\", default: false },\n check: { type: \"boolean\", default: false },\n update: { type: \"boolean\", default: false },\n show: { type: \"boolean\", default: false },\n range: { type: \"string\" },\n keep: { type: \"boolean\", default: false },\n \"output-dir\": { type: \"string\" },\n offline: { type: \"boolean\", default: false },\n \"carry-forward\": { type: \"string\", multiple: true },\n \"export-dichiarazione\": { type: \"string\" },\n },\n allowPositionals: true,\n strict: false,\n });\n\n // Resolve locale once — exits 2 on invalid value\n const locale = resolveLocale(values.lang as string | undefined);\n const s = getStrings(locale);\n\n const command = positionals[0];\n const restPositionals = positionals.slice(1);\n const flags = values as Record<string, string | boolean>;\n\n const stdout = process.stdout;\n const stderr = process.stderr;\n\n let exitCode = 0;\n\n try {\n switch (command) {\n case \"calc\":\n exitCode = await runCalc(restPositionals, flags, s, stdout, stderr);\n break;\n case \"validate\":\n exitCode = await runValidate(restPositionals, flags, s, stdout, stderr);\n break;\n case \"rates\":\n exitCode = await runRates(restPositionals, flags, s, stdout, stderr);\n break;\n case \"config\":\n exitCode = await runConfig(restPositionals, flags, s, stdout, stderr);\n break;\n case \"stress-test\":\n exitCode = await runStressTest(restPositionals, flags, stdout, stderr);\n break;\n case \"classify\":\n exitCode = await runClassify(restPositionals, flags, s, stdout, stderr);\n break;\n default:\n stderr.write(\n \"Usage: minus-tracker <calc|validate|rates|config|stress-test|classify> [options] [file]\\n\",\n );\n exitCode = 2;\n }\n } catch (err) {\n if (err instanceof ClassificationError) {\n stderr.write(err.message + \"\\n\");\n exitCode = 1;\n } else if (err instanceof ParseError) {\n if (err.code === \"INVALID_CSV\") {\n stderr.write(s.errorInvalidCsv + \"\\n\");\n } else {\n stderr.write(s.errorMissingColumn(err.columnName!) + \"\\n\");\n }\n exitCode = 1;\n } else if (err instanceof CalculationError) {\n stderr.write(s.errorNoOpenLots(err.isin, err.date) + \"\\n\");\n exitCode = 1;\n } else {\n throw err;\n }\n }\n\n process.exit(exitCode);\n}\n\nmain().catch((err) => {\n process.stderr.write(`Unhandled error: ${String(err)}\\n`);\n process.exit(1);\n});\n","import type { LocaleStrings } from \"./types.js\";\n\nexport const it: LocaleStrings = {\n numberLocale: \"it-IT\",\n\n errorInvalidCsv: \"CSV non valido: impossibile analizzare il file\",\n errorMissingColumn: (col) => `Colonna obbligatoria mancante: ${col}`,\n errorNoOpenLots: (isin, date) =>\n `Nessun lotto aperto per ISIN ${isin} in data ${date}`,\n errorCannotReadFile: (path) => `Impossibile leggere il file: ${path}`,\n errorCannotLoadSidecar: (path) => `Impossibile caricare il sidecar: ${path}`,\n errorCannotWriteExport: (path) =>\n `Impossibile scrivere l'export della dichiarazione: ${path}`,\n\n warnMissingIsin: (row) => `Riga ${row}: ISIN mancante — riga ignorata`,\n warnUnsupportedCurrency: (row, currency) =>\n `Riga ${row}: valuta non supportata ${currency} — riga ignorata`,\n warnNoEcbRate: (row, currency, date) =>\n `Riga ${row}: nessun tasso BCE per ${currency} in data ${date} — riga ignorata`,\n warnQuantityZero: (row) => `Riga ${row}: quantità pari a 0 — riga ignorata`,\n warnMissingIsinIncome: (row) =>\n `Riga ${row}: ISIN mancante su riga di reddito — riga ignorata`,\n warnOrphanWithholding: (isin, date) =>\n `Riga di ritenuta su ${isin}/${date} senza riga di reddito corrispondente — riga ignorata`,\n\n warnMultipleYears:\n \"Il CSV contiene transazioni di più anni — filtra per un singolo anno per un calcolo accurato.\",\n\n headerMethod: \"METODO\",\n headerTaxYear: \"ANNO FISCALE\",\n headerIsin: \"ISIN\",\n headerProduct: \"TITOLO\",\n headerQty: \"QTÀ\",\n headerBuyDate: \"DATA ACQUISTO\",\n headerSellDate: \"DATA VENDITA\",\n headerBuyEur: \"ACQUISTO EUR\",\n headerSellEur: \"VENDITA EUR\",\n headerGainLoss: \"GUADAGNO/PERDITA\",\n\n summaryPlusvalenze: \"PLUSVALENZE\",\n summaryMinusvalenze: \"MINUSVALENZE\",\n summaryNetResult: \"RISULTATO NETTO\",\n summaryWarnings: \"AVVERTENZE\",\n summaryGenerated: \"Generato\",\n\n validateOk: (count, errors) =>\n `OK: ${count} transazioni analizzate, ${errors} errori gravi`,\n validateWarn: (count, reason) =>\n `AVVISO: ${count} righe ignorate (${reason})`,\n\n ratesCoverage: (start, end, currencies) =>\n `Copertura: ${start} → ${end} | Valute: ${currencies}`,\n ratesGapsNone: \"Lacune: nessuna\",\n ratesGaps: (list) => `Lacune: ${list}`,\n ratesUpdateFetching: \"Recupero dati BCE SDMX in corso...\",\n ratesUpdateDone: (n) => `Completato. Aggiunte ${n} nuove date.`,\n ratesSnapshotWritten: (path) => `Snapshot scritto in ${path}`,\n ratesAutoUpdateStart:\n \"Le rate BCE non sono aggiornate. Aggiornamento automatico in corso...\",\n ratesAutoUpdateFailed:\n \"Aggiornamento automatico fallito. Si procede con le rate disponibili.\",\n\n configLangSet: (lang) => `Lingua impostata su: ${lang}`,\n configCurrentLang: (lang) => `Lingua corrente: ${lang}`,\n\n classifyStarting: (n) => `Classificazione ISIN in corso... (${n} strumenti)`,\n classifyDetected: (isin, product, bucket, rate) =>\n `Rilevato: ${product} (${isin}) → ${bucket} | ${rate}`,\n classifyConfirm: \"[Y/n/?]: \",\n classifyOverridePrompt: \"Tipo di strumento:\",\n classifyDone: (confirmed, total) =>\n `Classificazione completata (${confirmed}/${total} confermati).`,\n classifyWritten: (path) => `Scritto: ${path}`,\n classifyMergePrompt: (existing) =>\n `File già esistente (${existing} ISIN confermati). Aggiungere i nuovi ISIN? [Y/n]: `,\n classifyOfflineWarning: \"Modalità offline: ricerca OpenFIGI saltata.\",\n classifyUnknownType: (type) =>\n `Tipo non riconosciuto: ${type}. Classificare manualmente.`,\n classifyNonTtyError:\n \"Modalità interattiva richiede un terminale (TTY). Usa --offline per classificare senza terminale.\",\n\n bucketAHeader: \"BUCKET A — REDDITI DA CAPITALE (non compensabili)\",\n bucketBHeader: \"BUCKET B — REDDITI DIVERSI\",\n bucketAEtf: \"ETF (OICR)\",\n bucketABtpWl: \"BTP/WL\",\n bucketATotalTax: \"TOTALE IMPOSTA\",\n bucketBCarryApplied: (year) => `RIPORTO ${year}`,\n bucketBResult: \"RISULTATO\",\n bucketBCarryNote: \"(riportabile ai prossimi 4 anni)\",\n warnMixedBuckets:\n \"Le perdite in Bucket B non compensano le plusvalenze in Bucket A.\",\n warnMixedAssets:\n \"AVVISO: CSV contiene tipi di strumenti misti (es. ETF + Azioni).\\n\" +\n \" Il calcolo a bucket unico può non essere fiscalmente corretto.\\n\" +\n \" Esegui: minus-tracker classify trades.csv\",\n headerBucket: \"BUCKET\",\n warnUnclassifiedIsin: (isin) =>\n `ISIN ${isin} non trovato nella mappa di classificazione — assegnato a Bucket B.`,\n carryForwardInvalidFormat:\n \"Formato --carry-forward non valido. Usa: AAAA:importo (es. 2023:2500)\",\n\n disclaimer: \"minus-tracker è un ausilio al calcolo, non consulenza fiscale.\",\n\n // v0.7.0 dichiarazione section\n dichiarazioneHeader: (anno) =>\n `── MODELLO REDDITI PF [ANNO D'IMPOSTA: ${anno}]`,\n dichiarazioneNota:\n \"Nota: i valori riportano i codici riga indicativi — verificare la modulistica ufficiale.\",\n dichiarazioneWarningRow:\n \"⚠ I codici riga tra parentesi quadre sono indicativi. Verificare la versione corrente del modello.\",\n quadroRTHeader: \"QUADRO RT — SEZIONE II (redditi diversi)\",\n quadroRTPlusvalenze: \"[RT-P] Plusvalenze totali\",\n quadroRTMinusvalenze: \"[RT-M] Minusvalenze totali\",\n quadroRTDifferenza: \"[RT-D] Differenza\",\n quadroRTRiporto: (anno) => `[RT-C*] Riporto ${anno} (consumato)`,\n quadroRTImponibile: \"[RT-N] Imponibile netto\",\n quadroRTImposta: \"[RT-I] Imposta (26%)\",\n quadroRTRiportabile: \"[RT-R] Minusvalenze da riportare\",\n quadroRTPerdita: \"perdita netta:\",\n quadroRMHeader: \"QUADRO RM (redditi da capitale)\",\n quadroRMEtf26: \"[RM-A1] ETF/OICR plusvalenze (26%)\",\n quadroRMImposta: \"Imposta sostitutiva\",\n quadroRMBtp: \"[RM-A2] BTP/WL plusvalenze (12,5%)\",\n quadroRMDividendi: \"[RM-D] Dividendi esteri lordi\",\n quadroRMRitenuta: \"Ritenuta estera (credito)\",\n quadroRMCedole: \"[RM-C] Cedole obbligazionarie\",\n dichiarazioneDisclaimer:\n \"minus-tracker è un ausilio al calcolo, non consulenza fiscale.\",\n warnNoDichiarazioneSidecar:\n \"Suggerimento: esegui prima 'minus-tracker classify <file>' per abilitare il Quadro RT/RM.\",\n warnNoCarryForwardProvided: \"(eventuali perdite pregresse non applicate)\",\n};\n","import type { LocaleStrings } from \"./types.js\";\n\nexport const en: LocaleStrings = {\n numberLocale: \"en-US\",\n\n errorInvalidCsv: \"Invalid CSV: unable to parse\",\n errorMissingColumn: (col) => `Missing required column: ${col}`,\n errorNoOpenLots: (isin, date) => `No open lots for ISIN ${isin} on ${date}`,\n errorCannotReadFile: (path) => `Cannot read file: ${path}`,\n errorCannotLoadSidecar: (path) => `Cannot load sidecar: ${path}`,\n errorCannotWriteExport: (path) =>\n `Cannot write dichiarazione export: ${path}`,\n\n warnMissingIsin: (row) => `Row ${row}: missing ISIN — skipped`,\n warnUnsupportedCurrency: (row, currency) =>\n `Row ${row}: unsupported currency ${currency} — skipped`,\n warnNoEcbRate: (row, currency, date) =>\n `Row ${row}: no ECB rate for ${currency} on ${date} — skipped`,\n warnQuantityZero: (row) => `Row ${row}: quantity is 0 — skipped`,\n warnMissingIsinIncome: (row) =>\n `Row ${row}: income row has blank ISIN — skipped`,\n warnOrphanWithholding: (isin, date) =>\n `Withholding row on ${isin}/${date} has no matching income row — skipped`,\n\n warnMultipleYears:\n \"CSV contains transactions from multiple years — filter to a single year for accurate reporting.\",\n\n headerMethod: \"METHOD\",\n headerTaxYear: \"TAX YEAR\",\n headerIsin: \"ISIN\",\n headerProduct: \"PRODUCT\",\n headerQty: \"QTY\",\n headerBuyDate: \"BUY DATE\",\n headerSellDate: \"SELL DATE\",\n headerBuyEur: \"BUY EUR\",\n headerSellEur: \"SELL EUR\",\n headerGainLoss: \"GAIN/LOSS\",\n\n summaryPlusvalenze: \"PLUSVALENZE\",\n summaryMinusvalenze: \"MINUSVALENZE\",\n summaryNetResult: \"NET RESULT\",\n summaryWarnings: \"WARNINGS\",\n summaryGenerated: \"Generated\",\n\n validateOk: (count, errors) =>\n `OK: ${count} transactions parsed, ${errors} hard errors`,\n validateWarn: (count, reason) => `WARN: ${count} rows skipped (${reason})`,\n\n ratesCoverage: (start, end, currencies) =>\n `Coverage: ${start} → ${end} | Currencies: ${currencies}`,\n ratesGapsNone: \"Gaps: none\",\n ratesGaps: (list) => `Gaps: ${list}`,\n ratesUpdateFetching: \"Fetching ECB SDMX...\",\n ratesUpdateDone: (n) => `Done. Added ${n} new dates.`,\n ratesSnapshotWritten: (path) => `Snapshot written to ${path}`,\n ratesAutoUpdateStart: \"ECB rates are stale. Auto-updating…\",\n ratesAutoUpdateFailed: \"Auto-update failed. Proceeding with available rates.\",\n\n configLangSet: (lang) => `Language set to: ${lang}`,\n configCurrentLang: (lang) => `Current language: ${lang}`,\n\n classifyStarting: (n) => `Classifying ISINs... (${n} instruments)`,\n classifyDetected: (isin, product, bucket, rate) =>\n `Detected: ${product} (${isin}) → ${bucket} | ${rate}`,\n classifyConfirm: \"[Y/n/?]: \",\n classifyOverridePrompt: \"Instrument type:\",\n classifyDone: (confirmed, total) =>\n `Classification complete (${confirmed}/${total} confirmed).`,\n classifyWritten: (path) => `Written: ${path}`,\n classifyMergePrompt: (existing) =>\n `File already exists (${existing} confirmed ISINs). Add new ISINs? [Y/n]: `,\n classifyOfflineWarning: \"Offline mode: OpenFIGI lookup skipped.\",\n classifyUnknownType: (type) =>\n `Unrecognized type: ${type}. Please classify manually.`,\n classifyNonTtyError:\n \"Interactive mode requires a TTY. Use --offline to classify without a terminal.\",\n\n bucketAHeader: \"BUCKET A — CAPITAL INCOME (non-offsettable)\",\n bucketBHeader: \"BUCKET B — MISCELLANEOUS INCOME\",\n bucketAEtf: \"ETF (OICR)\",\n bucketABtpWl: \"Govt Bond WL\",\n bucketATotalTax: \"TOTAL TAX\",\n bucketBCarryApplied: (year) => `CARRY ${year}`,\n bucketBResult: \"RESULT\",\n bucketBCarryNote: \"(carriable over the next 4 years)\",\n warnMixedBuckets: \"Bucket B losses do not offset Bucket A plusvalenze.\",\n warnMixedAssets:\n \"WARNING: CSV contains mixed instrument types (e.g. ETFs + Stocks).\\n\" +\n \" Single-bucket calculation may not be fiscally correct.\\n\" +\n \" Run: minus-tracker classify trades.csv\",\n headerBucket: \"BUCKET\",\n warnUnclassifiedIsin: (isin) =>\n `ISIN ${isin} not found in classification map — assigned to Bucket B.`,\n carryForwardInvalidFormat:\n \"Invalid --carry-forward format. Use: YYYY:amount (e.g. 2023:2500)\",\n\n disclaimer: \"minus-tracker è un ausilio al calcolo, non consulenza fiscale.\",\n\n // v0.7.0 dichiarazione section\n dichiarazioneHeader: (anno) => `── MODELLO REDDITI PF [TAX YEAR: ${anno}]`,\n dichiarazioneNota:\n \"Note: row codes are indicative — verify against the official tax form.\",\n dichiarazioneWarningRow:\n \"⚠ Row codes in square brackets are indicative. Check the current form version.\",\n quadroRTHeader: \"QUADRO RT — SECTION II (capital gains)\",\n quadroRTPlusvalenze: \"[RT-P] Total gains\",\n quadroRTMinusvalenze: \"[RT-M] Total losses\",\n quadroRTDifferenza: \"[RT-D] Difference\",\n quadroRTRiporto: (anno) => `[RT-C*] Prior loss ${anno} (applied)`,\n quadroRTImponibile: \"[RT-N] Net taxable\",\n quadroRTImposta: \"[RT-I] Tax (26%)\",\n quadroRTRiportabile: \"[RT-R] Losses to carry forward\",\n quadroRTPerdita: \"net loss:\",\n quadroRMHeader: \"QUADRO RM (capital income)\",\n quadroRMEtf26: \"[RM-A1] ETF/OICR gains (26%)\",\n quadroRMImposta: \"Substitute tax\",\n quadroRMBtp: \"[RM-A2] BTP/WL gains (12.5%)\",\n quadroRMDividendi: \"[RM-D] Foreign dividends (gross)\",\n quadroRMRitenuta: \"Foreign withholding (credit)\",\n quadroRMCedole: \"[RM-C] Bond coupons\",\n dichiarazioneDisclaimer:\n \"minus-tracker è un ausilio al calcolo, non consulenza fiscale.\",\n warnNoDichiarazioneSidecar:\n \"Tip: run 'minus-tracker classify <file>' first to enable Quadro RT/RM.\",\n warnNoCarryForwardProvided: \"(prior-year losses not applied)\",\n};\n","import * as fs from \"node:fs\";\nimport * as os from \"node:os\";\nimport * as path from \"node:path\";\nimport type { MinusTrackerConfig, SupportedLocale } from \"./types.js\";\n\nconst SUPPORTED_LOCALES: SupportedLocale[] = [\"it\", \"en\"];\n\nexport function getConfigPath(): string {\n if (process.platform === \"win32\") {\n const appData =\n process.env.APPDATA ?? path.join(os.homedir(), \"AppData\", \"Roaming\");\n return path.join(appData, \"minus-tracker\", \"config.json\");\n }\n const xdg = process.env.XDG_CONFIG_HOME ?? path.join(os.homedir(), \".config\");\n return path.join(xdg, \"minus-tracker\", \"config.json\");\n}\n\nexport function readConfig(): MinusTrackerConfig {\n try {\n const raw = fs.readFileSync(getConfigPath(), \"utf8\");\n return JSON.parse(raw) as MinusTrackerConfig;\n } catch {\n return {};\n }\n}\n\nexport function saveLocale(lang: SupportedLocale): void {\n const configPath = getConfigPath();\n fs.mkdirSync(path.dirname(configPath), { recursive: true });\n const existing = readConfig();\n const updated: MinusTrackerConfig = { ...existing, locale: lang };\n fs.writeFileSync(configPath, JSON.stringify(updated, null, 2) + \"\\n\", \"utf8\");\n}\n\nexport function resolveLocale(cliLang?: string): SupportedLocale {\n // 1. --lang flag\n if (cliLang !== undefined) {\n if (!SUPPORTED_LOCALES.includes(cliLang as SupportedLocale)) {\n process.stderr.write(`Unsupported locale \"${cliLang}\". Use: it, en\\n`);\n process.exit(2);\n }\n return cliLang as SupportedLocale;\n }\n // 2. env var\n const envLang = process.env.MINUS_TRACKER_LANG;\n if (envLang !== undefined) {\n if (!SUPPORTED_LOCALES.includes(envLang as SupportedLocale)) {\n process.stderr.write(`Unsupported locale \"${envLang}\". Use: it, en\\n`);\n process.exit(2);\n }\n return envLang as SupportedLocale;\n }\n // 3. config file\n const config = readConfig();\n if (config.locale && SUPPORTED_LOCALES.includes(config.locale)) {\n return config.locale;\n }\n // 4. default\n return \"it\";\n}\n","import { it } from \"./it.js\";\nimport { en } from \"./en.js\";\nimport type { LocaleStrings, SupportedLocale } from \"./types.js\";\n\nexport { it, en };\nexport type { LocaleStrings, SupportedLocale };\nexport type { MinusTrackerConfig } from \"./types.js\";\nexport {\n resolveLocale,\n saveLocale,\n readConfig,\n getConfigPath,\n} from \"./settings.js\";\n\nexport function getStrings(locale: SupportedLocale): LocaleStrings {\n return locale === \"it\" ? it : en;\n}\n","export class ParseError extends Error {\n readonly code: \"INVALID_CSV\" | \"MISSING_COLUMN\";\n readonly columnName?: string;\n\n constructor(code: \"INVALID_CSV\");\n constructor(code: \"MISSING_COLUMN\", columnName: string);\n constructor(code: ParseError[\"code\"], columnName?: string) {\n const msg =\n code === \"INVALID_CSV\"\n ? \"Invalid CSV: unable to parse\"\n : `Missing required column: ${columnName}`;\n super(msg);\n this.name = \"ParseError\";\n this.code = code;\n this.columnName = columnName;\n }\n}\n\nexport class CalculationError extends Error {\n readonly isin: string;\n readonly date: string;\n\n constructor(isin: string, date: string) {\n super(`No open lots for ISIN ${isin} on ${date}`);\n this.name = \"CalculationError\";\n this.isin = isin;\n this.date = date;\n }\n}\n\nexport class ClassificationError extends Error {\n readonly code:\n | \"NETWORK_ERROR\"\n | \"SIDECAR_NOT_FOUND\"\n | \"SIDECAR_VERSION\"\n | \"SIDECAR_MALFORMED\"\n | \"WRITE_ERROR\";\n\n constructor(code: ClassificationError[\"code\"], message?: string) {\n super(\n message ??\n (code === \"SIDECAR_NOT_FOUND\"\n ? \"Sidecar file not found\"\n : code === \"SIDECAR_VERSION\"\n ? \"Sidecar version mismatch\"\n : code === \"SIDECAR_MALFORMED\"\n ? \"Sidecar file is malformed JSON\"\n : code === \"NETWORK_ERROR\"\n ? \"Network error contacting OpenFIGI\"\n : \"Failed to write sidecar file\"),\n );\n this.name = \"ClassificationError\";\n this.code = code;\n }\n}\n","import * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport * as os from \"node:os\";\nimport { DEGIROParser } from \"../../parser/index.js\";\nimport { Calculator } from \"../../calculator/index.js\";\nimport { Classifier } from \"../../classifier/index.js\";\nimport { ParseError } from \"../../errors.js\";\nimport { renderReport } from \"../renderer.js\";\nimport type { LocaleStrings } from \"../../i18n/types.js\";\nimport type {\n LotMethod,\n ClassificationMap,\n CarryForward,\n} from \"../../types.js\";\n\nexport async function runCalc(\n positional: string[],\n flags: Record<string, string | boolean>,\n s: LocaleStrings,\n stdout: NodeJS.WritableStream,\n stderr: NodeJS.WritableStream,\n): Promise<number> {\n const filePath = positional[0];\n if (!filePath) {\n stderr.write(\n \"Usage: minus-tracker calc [--method LIFO|FIFO] [--json] <file.csv>\\n\",\n );\n return 2;\n }\n\n let csv: string;\n try {\n csv = fs.readFileSync(filePath, \"utf8\");\n } catch {\n stderr.write(s.errorCannotReadFile(filePath) + \"\\n\");\n return 1;\n }\n\n const method = (flags[\"method\"] as LotMethod) ?? \"LIFO\";\n if (method !== \"LIFO\" && method !== \"FIFO\") {\n stderr.write(\"--method must be LIFO or FIFO\\n\");\n return 2;\n }\n\n // Sidecar auto-discovery\n const sidecarPath = filePath.replace(/\\.csv$/i, \"\") + \".classify.json\";\n let classification: ClassificationMap | undefined;\n if (fs.existsSync(sidecarPath)) {\n try {\n const classifier = new Classifier({ interactive: false });\n classification = await classifier.load(sidecarPath);\n } catch {\n stderr.write(s.errorCannotLoadSidecar(sidecarPath) + \"\\n\");\n return 1;\n }\n }\n\n // --export-dichiarazione flag parsing (optional path value)\n const exportFlagRaw = flags[\"export-dichiarazione\"];\n const exportRequested =\n exportFlagRaw !== undefined && exportFlagRaw !== false;\n const exportPath =\n typeof exportFlagRaw === \"string\"\n ? exportFlagRaw\n : filePath.replace(/\\.csv$/i, \"\") + \".dichiarazione.json\";\n\n if (exportRequested && classification === undefined) {\n stderr.write(s.warnNoDichiarazioneSidecar + \"\\n\");\n return 1;\n }\n\n // --carry-forward flag parsing\n const rawCf: unknown = flags[\"carry-forward\"];\n const rawCarryForwards: string[] = Array.isArray(rawCf)\n ? (rawCf as string[])\n : rawCf\n ? [rawCf as string]\n : [];\n\n const cfRegex = /^\\d{4}:\\d+(\\.\\d+)?$/;\n for (const cf of rawCarryForwards) {\n if (!cfRegex.test(cf)) {\n stderr.write(s.carryForwardInvalidFormat + \"\\n\");\n return 2;\n }\n }\n\n // Load carry-forward config file\n const xdgConfig =\n process.env[\"XDG_CONFIG_HOME\"] ?? path.join(os.homedir(), \".config\");\n const cfConfigPath = path.join(\n xdgConfig,\n \"minus-tracker\",\n \"carryforward.json\",\n );\n const cfFromFile: Record<number, number> = {};\n if (fs.existsSync(cfConfigPath)) {\n try {\n const raw = JSON.parse(fs.readFileSync(cfConfigPath, \"utf-8\")) as {\n losses?: Array<{ year: number; amount: number }>;\n };\n for (const entry of raw.losses ?? []) {\n cfFromFile[entry.year] = entry.amount;\n }\n } catch {\n /* silently skip malformed config */\n }\n }\n\n // Merge: flag wins over config file\n const cfMerged: Record<number, number> = { ...cfFromFile };\n for (const cf of rawCarryForwards) {\n const [yearStr, amountStr] = cf.split(\":\");\n cfMerged[parseInt(yearStr, 10)] = parseFloat(amountStr);\n }\n const carryForward: CarryForward[] = Object.entries(cfMerged).map(\n ([year, amount]) => ({ year: parseInt(year, 10), amount }),\n );\n\n const parser = new DEGIROParser();\n let transactions;\n try {\n transactions = parser.parse(csv);\n } catch (err) {\n if (err instanceof ParseError) {\n if (err.code === \"INVALID_CSV\") {\n stderr.write(s.errorInvalidCsv + \"\\n\");\n } else {\n stderr.write(s.errorMissingColumn(err.columnName!) + \"\\n\");\n }\n return 1;\n }\n throw err;\n }\n\n const calculator = new Calculator(transactions, parser.warnings, {\n classification,\n carryForward: carryForward.length > 0 ? carryForward : undefined,\n incomeRows: parser.incomeRows,\n });\n const report = calculator.calculateGains(method);\n const carryForwardWasProvided = carryForward.length > 0;\n\n if (exportRequested) {\n try {\n await report.dichiarazione!.exportTo(exportPath);\n } catch {\n stderr.write(s.errorCannotWriteExport(exportPath) + \"\\n\");\n return 1;\n }\n }\n\n if (flags[\"json\"]) {\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n const { dichiarazione: _dichiarazione, ...jsonReport } = report;\n stdout.write(JSON.stringify(jsonReport, null, 2) + \"\\n\");\n } else {\n stdout.write(renderReport(report, s, carryForwardWasProvided) + \"\\n\");\n }\n\n if (exportRequested) {\n stdout.write(s.classifyWritten(exportPath) + \"\\n\");\n } else if (!report.dichiarazione) {\n stderr.write(s.warnNoDichiarazioneSidecar + \"\\n\");\n }\n\n return 0;\n}\n","import { fileURLToPath } from \"node:url\";\nimport * as path from \"node:path\";\nimport * as fs from \"node:fs\";\n\nexport type RatesSnapshot = Record<string, Record<string, number>>;\n\n// Load the bundled snapshot lazily; returns null if the file is absent (e.g. broken install)\nlet _bundled: RatesSnapshot | null | undefined = undefined;\n\nfunction getBundledSnapshot(): RatesSnapshot | null {\n if (_bundled !== undefined) return _bundled;\n const __dirname = path.dirname(fileURLToPath(import.meta.url));\n const bundledPath = path.join(__dirname, \"../data/ecb-rates.json\");\n try {\n _bundled = JSON.parse(\n fs.readFileSync(bundledPath, \"utf8\"),\n ) as RatesSnapshot;\n } catch {\n _bundled = null;\n }\n return _bundled;\n}\n\nfunction getUserSnapshot(): RatesSnapshot | null {\n const platform = process.platform;\n let configDir: string;\n if (platform === \"win32\") {\n configDir =\n process.env.APPDATA ??\n path.join(process.env.USERPROFILE ?? \"~\", \"AppData\", \"Roaming\");\n } else {\n configDir =\n process.env.XDG_CONFIG_HOME ??\n path.join(process.env.HOME ?? \"~\", \".config\");\n }\n const userPath = path.join(configDir, \"minus-tracker\", \"ecb-rates.json\");\n try {\n return JSON.parse(fs.readFileSync(userPath, \"utf8\")) as RatesSnapshot;\n } catch {\n return null;\n }\n}\n\nexport function getActiveSnapshot(): RatesSnapshot {\n const bundled = getBundledSnapshot();\n const user = getUserSnapshot();\n if (!bundled && !user) {\n throw new Error(\n \"No ECB rates snapshot available. Run `minus-tracker rates --update` to fetch rates.\",\n );\n }\n if (!bundled) return user!;\n if (!user) return bundled;\n // Merge: user entries override bundled for same date keys\n const merged: RatesSnapshot = {};\n for (const ccy of new Set([...Object.keys(bundled), ...Object.keys(user)])) {\n merged[ccy] = { ...(bundled[ccy] ?? {}), ...(user[ccy] ?? {}) };\n }\n return merged;\n}\n\nexport function isSnapshotStale(\n snapshot: RatesSnapshot,\n today?: string,\n): boolean {\n const todayStr = today ?? new Date().toISOString().slice(0, 10);\n let maxDate = \"0000-01-01\";\n for (const dates of Object.values(snapshot)) {\n for (const d of Object.keys(dates)) {\n if (d > maxDate) maxDate = d;\n }\n }\n if (maxDate === \"0000-01-01\") return true;\n const diffDays =\n (new Date(todayStr).getTime() - new Date(maxDate).getTime()) /\n (1000 * 60 * 60 * 24);\n return diffDays >= 7;\n}\n\nfunction subtractDays(isoDate: string, days: number): string {\n const d = new Date(isoDate + \"T00:00:00Z\");\n d.setUTCDate(d.getUTCDate() - days);\n return d.toISOString().slice(0, 10);\n}\n\n/**\n * Look up ECB rate for a currency on a given date.\n * Returns 1.0 for EUR. Walks back up to 3 calendar days for weekend/holiday gaps.\n * Returns null if not found within the window.\n *\n * @param currency ISO 4217 currency code\n * @param date YYYY-MM-DD trade date\n * @param snapshot Optional override (used in tests to inject stub data)\n */\nexport function lookupRate(\n currency: string,\n date: string,\n snapshot?: RatesSnapshot,\n): number | null {\n if (currency === \"EUR\") return 1.0;\n\n const s = snapshot ?? getActiveSnapshot();\n const currencyRates = s[currency];\n if (!currencyRates) return null; // unsupported currency\n\n for (let i = 0; i <= 3; i++) {\n const d = i === 0 ? date : subtractDays(date, i);\n if (currencyRates[d] !== undefined) {\n return currencyRates[d];\n }\n }\n return null;\n}\n","export type WarningEntry =\n | { code: \"MISSING_ISIN\"; row: number }\n | { code: \"UNSUPPORTED_CURRENCY\"; row: number; currency: string }\n | { code: \"NO_ECB_RATE\"; row: number; currency: string; date: string }\n | { code: \"QUANTITY_ZERO\"; row: number }\n | { code: \"MISSING_ISIN_INCOME\"; row: number }\n | { code: \"ORPHAN_WITHHOLDING\"; isin: string; date: string };\n\nexport function warningToEnglish(w: WarningEntry): string {\n switch (w.code) {\n case \"MISSING_ISIN\":\n return `Row ${w.row}: missing ISIN — skipped`;\n case \"UNSUPPORTED_CURRENCY\":\n return `Row ${w.row}: unsupported currency ${w.currency} — skipped`;\n case \"NO_ECB_RATE\":\n return `Row ${w.row}: no ECB rate for ${w.currency} on ${w.date} — skipped`;\n case \"QUANTITY_ZERO\":\n return `Row ${w.row}: quantity is 0 — skipped`;\n case \"MISSING_ISIN_INCOME\":\n return `Row ${w.row}: blank ISIN on income row — skipped`;\n case \"ORPHAN_WITHHOLDING\":\n return `Withholding row for ${w.isin} on ${w.date}: no matching income row — skipped`;\n }\n}\n","import { Transaction, IncomeRow } from \"../types.js\";\nimport { ParseError } from \"../errors.js\";\nimport {\n lookupRate,\n getActiveSnapshot,\n RatesSnapshot,\n} from \"../rates/index.js\";\nimport { WarningEntry, warningToEnglish } from \"./warnings.js\";\n\nconst REQUIRED_COLUMNS = [\n \"ISIN\",\n \"Quantity\",\n \"Price\",\n \"Date\",\n \"Local value\",\n \"Local value currency\",\n \"Transaction costs\",\n \"Transaction costs currency\",\n \"Product\",\n] as const;\n\n// Income-row product keyword matching (case-insensitive substring match).\n// Tax keywords are checked first because \"DIVIDEND TAX\" also contains \"DIVIDEND\".\nconst INCOME_KEYWORDS = [\"DIVIDEND\", \"COUPON\", \"INTEREST\", \"CEDOLA\"] as const;\nconst TAX_KEYWORDS = [\"DIVIDEND TAX\", \"WITHHOLDING\", \"RITENUTA\"] as const;\n\ninterface IncomeCandidate {\n isin: string;\n product: string;\n date: string;\n incomeType: \"dividend\" | \"coupon\";\n localValue: number;\n currency: string;\n row: number;\n}\n\ninterface WithholdingCandidate {\n isin: string;\n date: string;\n amount: number; // absolute value, in source currency\n row: number;\n}\n\n/**\n * Parse a single CSV line, respecting RFC-4180-style double-quote escaping.\n * Does not support embedded newlines in fields (not needed for DEGIRO exports).\n */\nfunction parseCSVRow(line: string): string[] {\n const fields: string[] = [];\n let i = 0;\n\n while (i <= line.length) {\n // End of line — stop (handles trailing comma by not emitting extra empty field)\n if (i === line.length) break;\n\n if (line[i] === '\"') {\n // Quoted field\n let field = \"\";\n i++; // skip opening quote\n while (i < line.length) {\n if (line[i] === '\"' && i + 1 < line.length && line[i + 1] === '\"') {\n // Escaped double-quote\n field += '\"';\n i += 2;\n } else if (line[i] === '\"') {\n i++; // skip closing quote\n break;\n } else {\n field += line[i++];\n }\n }\n fields.push(field);\n if (i < line.length && line[i] === \",\") i++; // skip delimiter\n } else {\n // Unquoted field\n const start = i;\n while (i < line.length && line[i] !== \",\") i++;\n fields.push(line.slice(start, i));\n if (i < line.length) i++; // skip delimiter\n }\n }\n\n return fields;\n}\n\n/**\n * Parse a full CSV string (CRLF or LF line endings) into a 2-D array of strings.\n */\nfunction parseCSV(input: string): string[][] {\n const normalized = input.replace(/\\r\\n/g, \"\\n\").replace(/\\r/g, \"\\n\");\n return normalized.split(\"\\n\").map(parseCSVRow);\n}\n\n/**\n * Convert a DEGIRO date \"DD-MM-YYYY\" to ISO format \"YYYY-MM-DD\".\n */\nfunction parseDate(ddmmyyyy: string): string {\n const [dd, mm, yyyy] = ddmmyyyy.split(\"-\");\n return `${yyyy}-${mm}-${dd}`;\n}\n\n/**\n * Parses a DEGIRO Transactions CSV export into normalised Transaction objects.\n *\n * Export from Activity → Transactions in the DEGIRO UI (not the Account Statement).\n * Supported currencies: EUR, USD, GBP, CHF. ECB historical rates are used for conversion.\n */\nexport class DEGIROParser {\n private _warningEntries: WarningEntry[] = [];\n private _snapshot?: RatesSnapshot;\n private _incomeRows: IncomeRow[] = [];\n\n constructor(snapshot?: RatesSnapshot) {\n this._snapshot = snapshot;\n }\n\n /**\n * Parse a DEGIRO Transactions CSV export string.\n *\n * @param csv - Raw UTF-8 string from the DEGIRO Transactions export.\n * @returns Array of normalised Transaction objects. Empty if no data rows are found.\n * @throws {ParseError} code `\"INVALID_CSV\"` — malformed CSV or binary content.\n * @throws {ParseError} code `\"MISSING_COLUMN\"` (+ `columnName`) — required column absent.\n *\n * Rows with missing ISIN, unsupported currency, zero quantity, or no ECB rate within\n * 3 trading days are skipped silently. Inspect `parser.warnings` for details.\n */\n parse(csv: string): Transaction[] {\n this._warningEntries = [];\n this._incomeRows = [];\n\n // Binary content (null bytes) is not valid CSV\n if (typeof csv !== \"string\" || csv.includes(\"\\x00\")) {\n throw new ParseError(\"INVALID_CSV\");\n }\n\n let rows: string[][];\n try {\n rows = parseCSV(csv);\n } catch {\n throw new ParseError(\"INVALID_CSV\");\n }\n\n if (rows.length === 0) {\n throw new ParseError(\"INVALID_CSV\");\n }\n\n // Build column-name → index map from the header row\n const header = rows[0];\n const colIndex: Record<string, number> = {};\n for (let i = 0; i < header.length; i++) {\n colIndex[header[i].trim()] = i;\n }\n\n // Validate that every required column is present\n for (const col of REQUIRED_COLUMNS) {\n if (colIndex[col] === undefined) {\n throw new ParseError(\"MISSING_COLUMN\", col);\n }\n }\n\n // Resolve the rates snapshot once for the whole parse run\n const snapshot: RatesSnapshot = this._snapshot ?? getActiveSnapshot();\n\n const transactions: Transaction[] = [];\n const incomeCandidates: IncomeCandidate[] = [];\n const withholdingCandidates: WithholdingCandidate[] = [];\n\n for (let r = 1; r < rows.length; r++) {\n const row = rows[r];\n // 1-indexed row number: header = 1, first data row = 2\n const rowIndex = r + 1;\n\n // Skip completely empty rows (including the trailing empty line after a\n // final newline, and rows that are all whitespace)\n if (row.every((cell) => cell.trim() === \"\")) continue;\n\n const get = (col: string): string => (row[colIndex[col]] ?? \"\").trim();\n\n const isin = get(\"ISIN\");\n const product = get(\"Product\");\n const qtyStr = get(\"Quantity\");\n const rawQty = parseFloat(qtyStr);\n const qtyIsZeroOrBlank = qtyStr === \"\" || isNaN(rawQty) || rawQty === 0;\n\n // --- Income-row detection (Quantity == 0 or blank) ---\n if (qtyIsZeroOrBlank) {\n const upperProduct = product.toUpperCase();\n const isTaxKeyword = TAX_KEYWORDS.some((k) => upperProduct.includes(k));\n const isIncomeKeyword =\n !isTaxKeyword &&\n INCOME_KEYWORDS.some((k) => upperProduct.includes(k));\n\n if (isTaxKeyword || isIncomeKeyword) {\n const rawLocalValue = parseFloat(get(\"Local value\"));\n const localValue = isNaN(rawLocalValue) ? 0 : rawLocalValue;\n const isoDate = parseDate(get(\"Date\"));\n\n if (isTaxKeyword && localValue < 0) {\n if (!isin) {\n this._warningEntries.push({\n code: \"MISSING_ISIN_INCOME\",\n row: rowIndex,\n });\n continue;\n }\n withholdingCandidates.push({\n isin,\n date: isoDate,\n amount: Math.abs(localValue),\n row: rowIndex,\n });\n continue;\n }\n\n if (isIncomeKeyword && localValue > 0) {\n if (!isin) {\n this._warningEntries.push({\n code: \"MISSING_ISIN_INCOME\",\n row: rowIndex,\n });\n continue;\n }\n const incomeType: \"dividend\" | \"coupon\" = upperProduct.includes(\n \"DIVIDEND\",\n )\n ? \"dividend\"\n : \"coupon\";\n incomeCandidates.push({\n isin,\n product,\n date: isoDate,\n incomeType,\n localValue,\n currency: get(\"Local value currency\"),\n row: rowIndex,\n });\n continue;\n }\n // Keyword matched but sign doesn't match the expected direction —\n // fall through to standard transaction handling below.\n }\n }\n\n // --- ISIN ---\n if (!isin) {\n this._warningEntries.push({ code: \"MISSING_ISIN\", row: rowIndex });\n continue;\n }\n\n // --- Quantity ---\n if (isNaN(rawQty) || rawQty === 0) {\n this._warningEntries.push({ code: \"QUANTITY_ZERO\", row: rowIndex });\n continue;\n }\n\n const type: \"BUY\" | \"SELL\" = rawQty > 0 ? \"BUY\" : \"SELL\";\n const quantity = Math.abs(rawQty);\n\n // --- Date ---\n const isoDate = parseDate(get(\"Date\"));\n\n // --- Currency & FX ---\n const currency = get(\"Local value currency\");\n const rawTotalLocal = parseFloat(get(\"Local value\"));\n const totalLocal = isNaN(rawTotalLocal) ? 0 : rawTotalLocal;\n\n let totalEUR: number;\n let fxRate: number | undefined;\n\n if (currency === \"EUR\") {\n totalEUR = Math.abs(totalLocal);\n fxRate = undefined;\n } else {\n const rate = lookupRate(currency, isoDate, snapshot);\n if (rate === null) {\n if (snapshot[currency] === undefined) {\n this._warningEntries.push({\n code: \"UNSUPPORTED_CURRENCY\",\n row: rowIndex,\n currency,\n });\n } else {\n this._warningEntries.push({\n code: \"NO_ECB_RATE\",\n row: rowIndex,\n currency,\n date: isoDate,\n });\n }\n continue;\n }\n totalEUR = Math.abs(totalLocal) / rate;\n fxRate = rate;\n }\n\n // --- Fees ---\n const rawFees = parseFloat(get(\"Transaction costs\"));\n const feesEUR = Math.abs(isNaN(rawFees) ? 0 : rawFees);\n\n // --- Remaining fields ---\n const pricePerUnit = parseFloat(get(\"Price\"));\n\n transactions.push({\n isin,\n product,\n date: isoDate,\n type,\n quantity,\n pricePerUnit,\n currency,\n totalLocal,\n totalEUR,\n feesEUR,\n fxRate,\n });\n }\n\n // --- Pair withholding candidates with income candidates by (ISIN, date) ---\n // Order-independent: candidates were collected regardless of CSV row order.\n const withholdingByKey = new Map<\n string,\n { isin: string; date: string; amount: number }\n >();\n for (const w of withholdingCandidates) {\n const key = `${w.isin}|${w.date}`;\n const existing = withholdingByKey.get(key);\n withholdingByKey.set(key, {\n isin: w.isin,\n date: w.date,\n amount: (existing?.amount ?? 0) + w.amount,\n });\n }\n const consumedKeys = new Set<string>();\n\n const incomeRows: IncomeRow[] = [];\n for (const c of incomeCandidates) {\n const key = `${c.isin}|${c.date}`;\n const matchedWithholding = withholdingByKey.get(key);\n\n let grossAmount: number;\n let withholdingTax: number;\n let fxRate: number | undefined;\n\n if (c.currency === \"EUR\") {\n grossAmount = Math.abs(c.localValue);\n withholdingTax = matchedWithholding?.amount ?? 0;\n fxRate = undefined;\n } else {\n const rate = lookupRate(c.currency, c.date, snapshot);\n if (rate === null) {\n if (snapshot[c.currency] === undefined) {\n this._warningEntries.push({\n code: \"UNSUPPORTED_CURRENCY\",\n row: c.row,\n currency: c.currency,\n });\n } else {\n this._warningEntries.push({\n code: \"NO_ECB_RATE\",\n row: c.row,\n currency: c.currency,\n date: c.date,\n });\n }\n continue;\n }\n grossAmount = Math.abs(c.localValue) / rate;\n withholdingTax = matchedWithholding\n ? matchedWithholding.amount / rate\n : 0;\n fxRate = rate;\n }\n\n if (matchedWithholding) consumedKeys.add(key);\n\n incomeRows.push({\n isin: c.isin,\n product: c.product,\n date: c.date,\n incomeType: c.incomeType,\n grossAmount,\n withholdingTax,\n currency: c.currency,\n fxRate,\n });\n }\n\n // Orphan withholding candidates — no matching income row for (ISIN, date)\n for (const [key, w] of withholdingByKey) {\n if (!consumedKeys.has(key)) {\n this._warningEntries.push({\n code: \"ORPHAN_WITHHOLDING\",\n isin: w.isin,\n date: w.date,\n });\n }\n }\n\n this._incomeRows = incomeRows;\n\n return transactions;\n }\n\n get warnings(): string[] {\n return this._warningEntries.map(warningToEnglish);\n }\n\n get warningEntries(): WarningEntry[] {\n return this._warningEntries;\n }\n\n get incomeRows(): IncomeRow[] {\n return this._incomeRows;\n }\n}\n","import { writeFile } from \"node:fs/promises\";\nimport type {\n BucketAReport,\n BucketBReport,\n CarryForward,\n CarryForwardEntry,\n DichiarazioneReport,\n IncomeRow,\n QuadroRMReport,\n QuadroRTReport,\n} from \"../types.js\";\n\nfunction roundHalfUp(x: number): number {\n return (Math.sign(x) * Math.round(Math.abs(x) * 100)) / 100;\n}\n\nexport function buildQuadroRT(\n bucketB: BucketBReport,\n carryForward: CarryForward[],\n taxYear: number,\n): QuadroRTReport {\n const plusvalenze = bucketB.plusvalenze;\n const minusvalenze = bucketB.minusvalenze;\n const differenza = roundHalfUp(plusvalenze - minusvalenze);\n\n if (differenza > 0) {\n const sorted = [...carryForward].sort((a, b) => a.year - b.year);\n let remaining = differenza;\n const carryForwardApplied: CarryForwardEntry[] = [];\n\n for (const entry of sorted) {\n if (taxYear - entry.year > 4) continue;\n const consumed = Math.min(entry.amount, remaining);\n if (consumed > 0) {\n carryForwardApplied.push({\n annoOrigine: entry.year,\n importo: roundHalfUp(consumed),\n });\n remaining -= consumed;\n }\n }\n\n const imponibileNetto = roundHalfUp(remaining);\n const imposta = roundHalfUp(imponibileNetto * 0.26);\n\n return {\n plusvalenze,\n minusvalenze,\n differenza,\n carryForwardApplied,\n imponibileNetto,\n imposta,\n carryForwardRiportato: [],\n };\n } else if (differenza < 0) {\n return {\n plusvalenze,\n minusvalenze,\n differenza,\n carryForwardApplied: [],\n imponibileNetto: 0,\n imposta: 0,\n carryForwardRiportato: [\n { annoOrigine: taxYear, importo: roundHalfUp(Math.abs(differenza)) },\n ],\n };\n } else {\n return {\n plusvalenze,\n minusvalenze,\n differenza: 0,\n carryForwardApplied: [],\n imponibileNetto: 0,\n imposta: 0,\n carryForwardRiportato: [],\n };\n }\n}\n\nexport function buildQuadroRM(\n bucketA: BucketAReport | undefined,\n incomeRows: IncomeRow[],\n taxYear: number,\n): QuadroRMReport {\n const groups = bucketA?.groups ?? [];\n\n const group26 = groups.find((g) => g.taxRate === 0.26);\n const group125 = groups.find((g) => g.taxRate === 0.125);\n\n const capitaleAliquota26 = {\n plusvalenze: group26?.plusvalenze ?? 0,\n imposta: group26?.imposta ?? 0,\n };\n const capitaleAliquota125 = {\n plusvalenze: group125?.plusvalenze ?? 0,\n imposta: group125?.imposta ?? 0,\n };\n\n const dividendiEsteri = incomeRows\n .filter((r) => r.incomeType === \"dividend\")\n .map((r) => ({\n isin: r.isin,\n prodotto: r.product,\n lordo: r.grossAmount,\n rittenutaEstera: r.withholdingTax,\n }));\n\n const cedole = incomeRows\n .filter((r) => r.incomeType === \"coupon\")\n .map((r) => ({\n isin: r.isin,\n prodotto: r.product,\n importo: r.grossAmount,\n rittenutaEstera: r.withholdingTax,\n }));\n\n return {\n capitaleAliquota26,\n capitaleAliquota125,\n dividendiEsteri,\n cedole,\n };\n}\n\nexport function buildDichiarazioneReport(\n quadroRT: QuadroRTReport,\n quadroRM: QuadroRMReport,\n taxYear: number,\n): DichiarazioneReport {\n const version = 1;\n const modello = \"Redditi PF\" as const;\n const generatedAt = new Date().toISOString();\n\n return {\n version,\n annoImposta: taxYear,\n modello,\n generatedAt,\n quadroRT,\n quadroRM,\n exportTo: async (path: string) => {\n await writeFile(\n path,\n JSON.stringify(\n {\n version,\n annoImposta: taxYear,\n modello,\n generatedAt,\n quadroRT,\n quadroRM,\n },\n null,\n 2,\n ),\n );\n },\n };\n}\n","import type {\n Transaction,\n MatchedLot,\n GainsReport,\n LotMethod,\n CalculatorOptions,\n AssetClass,\n BucketAReport,\n BucketBReport,\n} from \"../types.js\";\nimport { CalculationError } from \"../errors.js\";\nimport {\n buildQuadroRT,\n buildQuadroRM,\n buildDichiarazioneReport,\n} from \"../dichiarazione/engine.js\";\n\ninterface Lot {\n date: string;\n quantity: number;\n pricePerUnitEUR: number;\n feesEUR: number;\n originalQty: number;\n fxRate?: number;\n}\n\nfunction roundHalfUp(x: number): number {\n return (Math.sign(x) * Math.round(Math.abs(x) * 100)) / 100;\n}\n\nfunction inferTaxYear(transactions: Transaction[]): {\n year: number;\n multipleYears: boolean;\n} {\n const counts: Record<string, number> = {};\n for (const t of transactions) {\n const y = t.date.slice(0, 4);\n counts[y] = (counts[y] ?? 0) + 1;\n }\n const years = Object.keys(counts);\n if (years.length === 0)\n return { year: new Date().getFullYear(), multipleYears: false };\n const year = parseInt(\n years.reduce((a, b) => (counts[a] >= counts[b] ? a : b)),\n );\n return { year, multipleYears: years.length > 1 };\n}\n\n/**\n * Computes Italian capital gains and losses from a list of parsed transactions.\n *\n * Uses LIFO or FIFO lot matching and converts all amounts to EUR via ECB rates.\n */\nexport class Calculator {\n private readonly _transactions: Transaction[];\n private readonly _parseWarnings: string[];\n private readonly _options: CalculatorOptions;\n\n constructor(\n transactions: Transaction[],\n parseWarnings?: string[],\n options?: CalculatorOptions,\n ) {\n this._transactions = transactions;\n this._parseWarnings = parseWarnings ?? [];\n this._options = options ?? {};\n }\n\n /**\n * Run LIFO or FIFO lot-matching over the transaction list.\n *\n * @param method - `\"LIFO\"` or `\"FIFO\"`.\n * @returns GainsReport with `plusvalenze`, `minusvalenze`, `netResult`, per-lot breakdown,\n * ECB rates used, and any accumulated warnings.\n * @throws {CalculationError} when a SELL has no matching open buy lots.\n * `error.isin` and `error.date` identify the problematic transaction.\n */\n calculateGains(method: LotMethod): GainsReport {\n const warnings: string[] = [...this._parseWarnings];\n\n // 1. Sort ascending by date; BUY before SELL on same date\n const sorted = [...this._transactions].sort((a, b) => {\n if (a.date < b.date) return -1;\n if (a.date > b.date) return 1;\n // Same date: BUY before SELL\n if (a.type === \"BUY\" && b.type === \"SELL\") return -1;\n if (a.type === \"SELL\" && b.type === \"BUY\") return 1;\n return 0;\n });\n\n // 2. Tax year inference\n const { year: taxYear, multipleYears } = inferTaxYear(sorted);\n if (multipleYears) {\n warnings.push(\n \"CSV contains transactions from multiple years — filter to a single year for accurate reporting.\",\n );\n }\n\n // 3. Lot matching\n const openLots = new Map<string, Lot[]>();\n const matchedLots: MatchedLot[] = [];\n const ratesUsed: Record<string, number> = {};\n\n for (const tx of sorted) {\n // Collect rates used\n if (tx.fxRate !== undefined) {\n ratesUsed[`${tx.currency}:${tx.date}`] = tx.fxRate;\n }\n\n if (tx.type === \"BUY\") {\n const lot: Lot = {\n date: tx.date,\n quantity: tx.quantity,\n pricePerUnitEUR: tx.totalEUR / tx.quantity,\n feesEUR: tx.feesEUR,\n originalQty: tx.quantity,\n fxRate: tx.fxRate,\n };\n if (!openLots.has(tx.isin)) openLots.set(tx.isin, []);\n openLots.get(tx.isin)!.push(lot);\n } else {\n // SELL\n const lots = openLots.get(tx.isin);\n if (!lots || lots.length === 0) {\n throw new CalculationError(tx.isin, tx.date);\n }\n\n const sellPricePerUnitEUR = tx.totalEUR / tx.quantity;\n let remainingSellQty = tx.quantity;\n\n while (remainingSellQty > 0) {\n if (!lots || lots.length === 0) {\n throw new CalculationError(tx.isin, tx.date);\n }\n\n const lot = method === \"LIFO\" ? lots[lots.length - 1] : lots[0];\n const matchedQty = Math.min(lot.quantity, remainingSellQty);\n\n // Fee allocation\n const allocatedBuyFeesEUR =\n lot.feesEUR * (matchedQty / lot.originalQty);\n const allocatedSellFeesEUR = tx.feesEUR * (matchedQty / tx.quantity);\n\n const buyCostEUR =\n lot.pricePerUnitEUR * matchedQty + allocatedBuyFeesEUR;\n const sellProceedsEUR =\n sellPricePerUnitEUR * matchedQty - allocatedSellFeesEUR;\n const gainLossEUR = sellProceedsEUR - buyCostEUR;\n\n matchedLots.push({\n isin: tx.isin,\n product: tx.product,\n quantity: matchedQty,\n buyDate: lot.date,\n sellDate: tx.date,\n buyPriceEUR: lot.pricePerUnitEUR,\n sellPriceEUR: sellPricePerUnitEUR,\n buyCostEUR: roundHalfUp(buyCostEUR),\n sellProceedsEUR: roundHalfUp(sellProceedsEUR),\n gainLossEUR: roundHalfUp(gainLossEUR),\n buyFxRate: lot.fxRate,\n sellFxRate: tx.fxRate,\n });\n\n lot.quantity -= matchedQty;\n remainingSellQty -= matchedQty;\n\n if (lot.quantity <= 0) {\n if (method === \"LIFO\") {\n lots.pop();\n } else {\n lots.shift();\n }\n }\n }\n }\n }\n\n // 4. Aggregate\n let plusvalenze = 0;\n let minusvalenze = 0;\n for (const lot of matchedLots) {\n if (lot.gainLossEUR > 0) plusvalenze += lot.gainLossEUR;\n else minusvalenze += Math.abs(lot.gainLossEUR);\n }\n\n // Two-bucket routing (only when classification map provided)\n if (this._options.classification) {\n const classification = this._options.classification;\n const unclassifiedIsins = new Set<string>();\n\n for (const lot of matchedLots) {\n const entry = classification[lot.isin];\n if (!entry) {\n lot.bucket = \"B\";\n unclassifiedIsins.add(lot.isin);\n } else if (entry.bucketGain === \"A\" && lot.gainLossEUR >= 0) {\n lot.bucket = \"A\";\n } else {\n lot.bucket = \"B\";\n }\n }\n\n for (const isin of unclassifiedIsins) {\n warnings.push(\n `ISIN ${isin} not found in classification map — assigned to Bucket B.`,\n );\n }\n\n // Bucket A computation\n const bucketALots = matchedLots.filter((l) => l.bucket === \"A\");\n const groupsByRate = new Map<\n number,\n { assetClasses: Set<string>; plusvalenze: number }\n >();\n for (const lot of bucketALots) {\n const entry = classification[lot.isin]!;\n const rate = entry.taxRate;\n if (!groupsByRate.has(rate))\n groupsByRate.set(rate, { assetClasses: new Set(), plusvalenze: 0 });\n const g = groupsByRate.get(rate)!;\n g.assetClasses.add(entry.assetClass);\n g.plusvalenze += lot.gainLossEUR;\n }\n const bucketAGroups = [...groupsByRate.entries()].map(([taxRate, g]) => ({\n taxRate,\n assetClasses: [...g.assetClasses] as AssetClass[],\n plusvalenze: roundHalfUp(g.plusvalenze),\n imposta: roundHalfUp(g.plusvalenze * taxRate),\n }));\n const bucketAReport: BucketAReport = {\n groups: bucketAGroups,\n totalImposta: roundHalfUp(\n bucketAGroups.reduce((s, g) => s + g.imposta, 0),\n ),\n };\n\n // Bucket B computation\n const bucketBLots = matchedLots.filter((l) => l.bucket === \"B\");\n let bPlusvalenze = 0;\n let bMinusvalenze = 0;\n for (const lot of bucketBLots) {\n if (lot.gainLossEUR >= 0) bPlusvalenze += lot.gainLossEUR;\n else bMinusvalenze += Math.abs(lot.gainLossEUR);\n }\n bPlusvalenze = roundHalfUp(bPlusvalenze);\n bMinusvalenze = roundHalfUp(bMinusvalenze);\n\n const carryForwards = [...(this._options.carryForward ?? [])].sort(\n (a, b) => a.year - b.year,\n );\n let remaining = bPlusvalenze - bMinusvalenze;\n let carryForwardApplied = 0;\n for (const entry of carryForwards) {\n if (taxYear - entry.year > 4) continue;\n if (remaining <= 0) break;\n const consumed = Math.min(entry.amount, remaining);\n carryForwardApplied += consumed;\n remaining -= consumed;\n }\n carryForwardApplied = roundHalfUp(carryForwardApplied);\n const bNetResult = roundHalfUp(\n bPlusvalenze - bMinusvalenze - carryForwardApplied,\n );\n const carryForwardRemaining = roundHalfUp(Math.max(0, -bNetResult));\n\n const bucketBReport: BucketBReport = {\n plusvalenze: bPlusvalenze,\n minusvalenze: bMinusvalenze,\n carryForwardApplied,\n carryForwardRemaining,\n netResult: bNetResult,\n };\n\n // Filter income rows to the tax year and build the dichiarazione report\n const allIncomeRows = this._options.incomeRows ?? [];\n const filteredIncomeRows = allIncomeRows.filter(\n (row) => new Date(row.date).getFullYear() === taxYear,\n );\n if (filteredIncomeRows.length < allIncomeRows.length) {\n warnings.push(`Income rows outside tax year ${taxYear} were skipped.`);\n }\n\n const quadroRT = buildQuadroRT(\n bucketBReport,\n this._options.carryForward ?? [],\n taxYear,\n );\n const quadroRM = buildQuadroRM(\n bucketAGroups.length > 0 ? bucketAReport : undefined,\n filteredIncomeRows,\n taxYear,\n );\n const dichiarazioneReport = buildDichiarazioneReport(\n quadroRT,\n quadroRM,\n taxYear,\n );\n\n return {\n method,\n taxYear,\n plusvalenze: roundHalfUp(plusvalenze),\n minusvalenze: roundHalfUp(minusvalenze),\n netResult: roundHalfUp(plusvalenze - minusvalenze),\n lots: matchedLots,\n ratesUsed,\n warnings,\n generatedAt: new Date().toISOString(),\n bucketA: bucketAGroups.length > 0 ? bucketAReport : undefined,\n bucketB: bucketBReport,\n dichiarazione: dichiarazioneReport,\n };\n } else {\n // Mixed-asset heuristic (no classification map)\n const allIsins = [...new Set(this._transactions.map((t) => t.isin))];\n const hasIePrefix = allIsins.some((isin) => isin.startsWith(\"IE\"));\n const hasOther = allIsins.some((isin) => !isin.startsWith(\"IE\"));\n if (hasIePrefix && hasOther) {\n warnings.push(\n \"AVVISO: CSV contiene tipi di strumenti misti (es. ETF + Azioni).\\n\" +\n \" Il calcolo a bucket unico può non essere fiscalmente corretto.\\n\" +\n \" Esegui: minus-tracker classify trades.csv\",\n );\n }\n }\n\n return {\n method,\n taxYear,\n plusvalenze: roundHalfUp(plusvalenze),\n minusvalenze: roundHalfUp(minusvalenze),\n netResult: roundHalfUp(plusvalenze - minusvalenze),\n lots: matchedLots,\n ratesUsed,\n warnings,\n generatedAt: new Date().toISOString(),\n };\n }\n}\n","import { ClassificationError } from \"../errors.js\";\nimport type {\n AssetClass,\n ClassificationEntry,\n ClassificationMap,\n ClassifyOptions,\n Transaction,\n} from \"../types.js\";\nimport * as fs from \"node:fs\";\nimport * as https from \"node:https\";\n\ntype HttpPost = (\n url: string,\n body: string,\n timeoutMs: number,\n) => Promise<{ status: number; data: string }>;\n\nfunction httpsPost(\n url: string,\n body: string,\n timeoutMs: number,\n): Promise<{ status: number; data: string }> {\n return new Promise((resolve, reject) => {\n const u = new URL(url);\n const req = https.request(\n {\n hostname: u.hostname,\n path: u.pathname + u.search,\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"Content-Length\": Buffer.byteLength(body),\n },\n },\n (res) => {\n let data = \"\";\n res.on(\"data\", (chunk) => {\n data += chunk;\n });\n res.on(\"end\", () => resolve({ status: res.statusCode ?? 0, data }));\n },\n );\n req.setTimeout(timeoutMs, () => {\n req.destroy();\n reject(new Error(\"timeout\"));\n });\n req.on(\"error\", reject);\n req.write(body);\n req.end();\n });\n}\n\nconst GOVT_BOND_WHITELIST = new Set([\n \"IT\",\n \"DE\",\n \"FR\",\n \"AT\",\n \"BE\",\n \"NL\",\n \"ES\",\n \"PT\",\n \"FI\",\n \"IE\",\n \"LU\",\n \"GR\",\n \"SK\",\n \"SI\",\n \"LT\",\n \"LV\",\n \"EE\",\n \"MT\",\n \"CY\",\n \"US\",\n \"GB\",\n \"CH\",\n \"NO\",\n \"SE\",\n \"DK\",\n \"JP\",\n \"CA\",\n \"AU\",\n \"NZ\",\n \"SG\",\n \"HK\",\n \"KR\",\n]);\n\ntype TypeMapping = {\n assetClass: AssetClass;\n bucketGain: \"A\" | \"B\";\n bucketLoss: \"A\" | \"B\";\n taxRate: number;\n whiteListed: boolean | null;\n};\n\nconst SECURITY_TYPE_MAP: Record<string, TypeMapping> = {\n ETP: {\n assetClass: \"ETF\",\n bucketGain: \"A\",\n bucketLoss: \"B\",\n taxRate: 0.26,\n whiteListed: null,\n },\n ETF: {\n assetClass: \"ETF\",\n bucketGain: \"A\",\n bucketLoss: \"B\",\n taxRate: 0.26,\n whiteListed: null,\n },\n \"Mutual Fund\": {\n assetClass: \"ETF\",\n bucketGain: \"A\",\n bucketLoss: \"B\",\n taxRate: 0.26,\n whiteListed: null,\n },\n \"Open-End Fund\": {\n assetClass: \"ETF\",\n bucketGain: \"A\",\n bucketLoss: \"B\",\n taxRate: 0.26,\n whiteListed: null,\n },\n \"Common Stock\": {\n assetClass: \"Stock\",\n bucketGain: \"B\",\n bucketLoss: \"B\",\n taxRate: 0,\n whiteListed: null,\n },\n Equity: {\n assetClass: \"Stock\",\n bucketGain: \"B\",\n bucketLoss: \"B\",\n taxRate: 0,\n whiteListed: null,\n },\n \"Exchange Traded Commodity\": {\n assetClass: \"ETC\",\n bucketGain: \"B\",\n bucketLoss: \"B\",\n taxRate: 0,\n whiteListed: null,\n },\n \"Corporate Bond\": {\n assetClass: \"CorpBond\",\n bucketGain: \"B\",\n bucketLoss: \"B\",\n taxRate: 0,\n whiteListed: null,\n },\n Option: {\n assetClass: \"Derivative\",\n bucketGain: \"B\",\n bucketLoss: \"B\",\n taxRate: 0,\n whiteListed: null,\n },\n Future: {\n assetClass: \"Derivative\",\n bucketGain: \"B\",\n bucketLoss: \"B\",\n taxRate: 0,\n whiteListed: null,\n },\n Warrant: {\n assetClass: \"Derivative\",\n bucketGain: \"B\",\n bucketLoss: \"B\",\n taxRate: 0,\n whiteListed: null,\n },\n CFD: {\n assetClass: \"Derivative\",\n bucketGain: \"B\",\n bucketLoss: \"B\",\n taxRate: 0,\n whiteListed: null,\n },\n Swap: {\n assetClass: \"Derivative\",\n bucketGain: \"B\",\n bucketLoss: \"B\",\n taxRate: 0,\n whiteListed: null,\n },\n \"Leverage Certificate\": {\n assetClass: \"LeverageCert\",\n bucketGain: \"B\",\n bucketLoss: \"B\",\n taxRate: 0,\n whiteListed: null,\n },\n \"Turbo Certificate\": {\n assetClass: \"LeverageCert\",\n bucketGain: \"B\",\n bucketLoss: \"B\",\n taxRate: 0,\n whiteListed: null,\n },\n \"Capital Protected Certificate\": {\n assetClass: \"CapProtectedCert\",\n bucketGain: \"A\",\n bucketLoss: \"B\",\n taxRate: 0.26,\n whiteListed: null,\n },\n};\n\nfunction isKnownType(t: string): boolean {\n return t === \"Government Bond\" || t in SECURITY_TYPE_MAP;\n}\n\nfunction classifyByType(\n isin: string,\n securityType: string,\n product: string,\n warnings: string[],\n): ClassificationEntry {\n if (securityType === \"Government Bond\") {\n const prefix = isin.slice(0, 2).toUpperCase();\n const whiteListed = GOVT_BOND_WHITELIST.has(prefix);\n return {\n product,\n assetClass: whiteListed ? \"GovtBondWL\" : \"GovtBondOther\",\n bucketGain: \"A\",\n bucketLoss: \"B\",\n taxRate: whiteListed ? 0.125 : 0.26,\n whiteListed,\n confirmedByUser: false,\n source: \"openfigi\",\n };\n }\n\n const mapped = SECURITY_TYPE_MAP[securityType];\n if (mapped) {\n return {\n product,\n assetClass: mapped.assetClass,\n bucketGain: mapped.bucketGain,\n bucketLoss: mapped.bucketLoss,\n taxRate: mapped.taxRate,\n whiteListed: mapped.whiteListed,\n confirmedByUser: false,\n source: \"openfigi\",\n };\n }\n\n // Unknown type — flag for manual review\n warnings.push(\n `Unrecognized type for ${isin}: ${securityType}. Please classify manually.`,\n );\n return {\n product,\n assetClass: \"Stock\",\n bucketGain: \"B\",\n bucketLoss: \"B\",\n taxRate: 0,\n whiteListed: null,\n confirmedByUser: false,\n source: \"user\",\n };\n}\n\nconst ASSET_CLASS_DEFAULTS: Record<AssetClass, TypeMapping> = {\n ETF: {\n assetClass: \"ETF\",\n bucketGain: \"A\",\n bucketLoss: \"B\",\n taxRate: 0.26,\n whiteListed: null,\n },\n Stock: {\n assetClass: \"Stock\",\n bucketGain: \"B\",\n bucketLoss: \"B\",\n taxRate: 0,\n whiteListed: null,\n },\n ETC: {\n assetClass: \"ETC\",\n bucketGain: \"B\",\n bucketLoss: \"B\",\n taxRate: 0,\n whiteListed: null,\n },\n GovtBondWL: {\n assetClass: \"GovtBondWL\",\n bucketGain: \"A\",\n bucketLoss: \"B\",\n taxRate: 0.125,\n whiteListed: true,\n },\n GovtBondOther: {\n assetClass: \"GovtBondOther\",\n bucketGain: \"A\",\n bucketLoss: \"B\",\n taxRate: 0.26,\n whiteListed: false,\n },\n CorpBond: {\n assetClass: \"CorpBond\",\n bucketGain: \"B\",\n bucketLoss: \"B\",\n taxRate: 0,\n whiteListed: null,\n },\n Derivative: {\n assetClass: \"Derivative\",\n bucketGain: \"B\",\n bucketLoss: \"B\",\n taxRate: 0,\n whiteListed: null,\n },\n LeverageCert: {\n assetClass: \"LeverageCert\",\n bucketGain: \"B\",\n bucketLoss: \"B\",\n taxRate: 0,\n whiteListed: null,\n },\n CapProtectedCert: {\n assetClass: \"CapProtectedCert\",\n bucketGain: \"A\",\n bucketLoss: \"B\",\n taxRate: 0.26,\n whiteListed: null,\n },\n};\n\n/**\n * Builds a user-confirmed `ClassificationEntry` from a caller-supplied\n * `AssetClass` override (v0.8.0 stateless mode). The caller picked the asset\n * class directly, so no ISIN-prefix whitelist lookup is performed here — the\n * caller owns gov-bond-whitelisted-vs-other correctness.\n */\nfunction buildEntryFromAssetClass(\n assetClass: AssetClass,\n product: string,\n): ClassificationEntry {\n const mapped = ASSET_CLASS_DEFAULTS[assetClass];\n return {\n product,\n assetClass: mapped.assetClass,\n bucketGain: mapped.bucketGain,\n bucketLoss: mapped.bucketLoss,\n taxRate: mapped.taxRate,\n whiteListed: mapped.whiteListed,\n confirmedByUser: true,\n source: \"user\",\n };\n}\n\ntype OpenFIGIItem = { securityType?: string; securityType2?: string };\ntype OpenFIGIResult = { data?: OpenFIGIItem[]; error?: string };\n\nexport class Classifier {\n private readonly interactive: boolean;\n\n constructor(options?: { interactive?: boolean }) {\n this.interactive = options?.interactive ?? true;\n }\n\n async load(sidecarPath: string): Promise<ClassificationMap> {\n if (!fs.existsSync(sidecarPath)) {\n throw new ClassificationError(\"SIDECAR_NOT_FOUND\");\n }\n\n let parsed: unknown;\n try {\n const raw = fs.readFileSync(sidecarPath, \"utf-8\");\n parsed = JSON.parse(raw);\n } catch {\n throw new ClassificationError(\"SIDECAR_MALFORMED\");\n }\n\n if (\n typeof parsed !== \"object\" ||\n parsed === null ||\n (parsed as Record<string, unknown>)[\"version\"] !== 1\n ) {\n throw new ClassificationError(\"SIDECAR_VERSION\");\n }\n\n return (parsed as { classifications: ClassificationMap }).classifications;\n }\n\n async classify(\n transactions: Transaction[],\n sidecarPath?: string,\n options?: ClassifyOptions,\n _httpPost: HttpPost = httpsPost,\n ): Promise<ClassificationMap> {\n // 1. Deduplicate ISINs; track first product name seen per ISIN\n const isinToProduct = new Map<string, string>();\n for (const tx of transactions) {\n if (tx.isin && !isinToProduct.has(tx.isin)) {\n isinToProduct.set(tx.isin, tx.product);\n }\n }\n\n // 2. Build the \"already resolved\" set.\n // Sidecar mode (unchanged v0.6.0 behavior): only confirmedByUser:true\n // entries from disk are trusted; everything else is re-processed.\n // Stateless mode (sidecarPath undefined, v0.8.0): no disk access at\n // all — ALL entries in options.existingClassification are trusted\n // as-is, regardless of confirmedByUser.\n const confirmed: ClassificationMap = {};\n\n if (sidecarPath !== undefined) {\n if (fs.existsSync(sidecarPath)) {\n const existingMap = await this.load(sidecarPath);\n for (const [isin, entry] of Object.entries(existingMap)) {\n if (entry.confirmedByUser) {\n confirmed[isin] = entry;\n }\n }\n }\n } else if (options?.existingClassification) {\n Object.assign(confirmed, options.existingClassification);\n }\n\n // overrides always win, in both modes, and are never sent to OpenFIGI\n for (const [isin, assetClass] of Object.entries(options?.overrides ?? {})) {\n const product = isinToProduct.get(isin) ?? isin;\n confirmed[isin] = buildEntryFromAssetClass(assetClass, product);\n }\n\n // 3. ISINs that need (re-)processing: not already resolved\n const toProcess: string[] = [];\n for (const isin of isinToProduct.keys()) {\n if (!(isin in confirmed)) {\n toProcess.push(isin);\n }\n }\n\n // 4. Batch-query OpenFIGI (skipped entirely in offline mode)\n const warnings: string[] = [];\n const newEntries: ClassificationMap = {};\n\n if (options?.offline) {\n for (const isin of toProcess) {\n const product = isinToProduct.get(isin) ?? isin;\n warnings.push(\n `Unrecognized type for ${isin}: unknown. Please classify manually.`,\n );\n newEntries[isin] = {\n product,\n assetClass: \"Stock\",\n bucketGain: \"B\",\n bucketLoss: \"B\",\n taxRate: 0,\n whiteListed: null,\n confirmedByUser: false,\n source: \"user\",\n };\n }\n } else {\n const BATCH_SIZE = 10;\n const totalBatches = Math.ceil(toProcess.length / BATCH_SIZE);\n\n for (let i = 0; i < toProcess.length; i += BATCH_SIZE) {\n // Wait 6 s between consecutive batch requests\n if (i > 0) {\n await new Promise<void>((resolve) => setTimeout(resolve, 6000));\n }\n\n const batch = toProcess.slice(i, i + BATCH_SIZE);\n const requestBody = JSON.stringify(\n batch.map((isin) => ({ idType: \"ID_ISIN\", idValue: isin })),\n );\n\n let response: { status: number; data: string } | null = null;\n\n // Try once; retry once on 5xx\n for (let attempt = 0; attempt < 2; attempt++) {\n try {\n const r = await _httpPost(\n \"https://api.openfigi.com/v3/mapping\",\n requestBody,\n 10_000,\n );\n if (r.status < 500) {\n response = r;\n break;\n }\n // 5xx — loop will retry once\n } catch {\n // Network-level error — no retry\n throw new ClassificationError(\"NETWORK_ERROR\");\n }\n }\n\n if (response === null) {\n throw new ClassificationError(\"NETWORK_ERROR\");\n }\n\n const results = JSON.parse(response.data) as OpenFIGIResult[];\n\n for (let j = 0; j < batch.length; j++) {\n const isin = batch[j]!;\n const result = results[j];\n const product = isinToProduct.get(isin) ?? isin;\n\n if (\n !result ||\n result.error ||\n !result.data ||\n result.data.length === 0\n ) {\n warnings.push(\n `Unrecognized type for ${isin}: unknown. Please classify manually.`,\n );\n newEntries[isin] = {\n product,\n assetClass: \"Stock\",\n bucketGain: \"B\",\n bucketLoss: \"B\",\n taxRate: 0,\n whiteListed: null,\n confirmedByUser: false,\n source: \"user\",\n };\n continue;\n }\n\n const st = result.data[0]?.securityType;\n const st2 = result.data[0]?.securityType2;\n\n // Prefer the type that resolves to a known mapping\n const typeToUse =\n st && isKnownType(st)\n ? st\n : st2 && isKnownType(st2)\n ? st2\n : (st ?? st2 ?? \"Unknown\");\n\n newEntries[isin] = classifyByType(isin, typeToUse, product, warnings);\n }\n\n const batchIndex = i / BATCH_SIZE;\n options?.onBatchProgress?.(batchIndex + 1, totalBatches);\n }\n }\n\n // 5. Merge: confirmed/overrides entries take precedence and remain untouched\n const mergedMap: ClassificationMap = { ...newEntries, ...confirmed };\n\n // 6. Write sidecar — only in sidecar mode; stateless mode never touches disk\n if (sidecarPath !== undefined) {\n const sidecarContent = JSON.stringify(\n {\n version: 1,\n generatedAt: new Date().toISOString(),\n classifications: mergedMap,\n warnings,\n },\n null,\n 2,\n );\n\n try {\n fs.writeFileSync(sidecarPath, sidecarContent, \"utf-8\");\n } catch {\n throw new ClassificationError(\"WRITE_ERROR\");\n }\n }\n\n return mergedMap;\n }\n}\n","import type { GainsReport, AssetClass } from \"../types.js\";\nimport type { LocaleStrings } from \"../i18n/types.js\";\n\nconst SEPARATOR = \"─\".repeat(72);\n\nfunction formatEUR(amount: number, locale: string): string {\n return new Intl.NumberFormat(locale, {\n minimumFractionDigits: 2,\n maximumFractionDigits: 2,\n }).format(amount);\n}\n\nfunction formatGainLoss(amount: number, locale: string): string {\n const formatted = formatEUR(Math.abs(amount), locale);\n return amount >= 0 ? `+${formatted}` : `-${formatted}`;\n}\n\nfunction assetClassLabel(ac: AssetClass, s: LocaleStrings): string {\n if (ac === \"ETF\") return s.bucketAEtf;\n if (ac === \"GovtBondWL\" || ac === \"GovtBondOther\") return s.bucketABtpWl;\n return ac;\n}\n\n/**\n * Renders the \"MODELLO REDDITI PF\" declaration section (Quadro RT + Quadro RM)\n * appended to the calc output when a classification sidecar is present.\n * See docs/prd/14-dichiarazione-engine.md \"CLI Output Section\".\n */\nfunction renderDichiarazione(\n report: GainsReport,\n s: LocaleStrings,\n carryForwardWasProvided: boolean,\n): string {\n const d = report.dichiarazione!;\n const rt = d.quadroRT;\n const rm = d.quadroRM;\n const fmt = (n: number) => formatEUR(n, s.numberLocale);\n const lines: string[] = [];\n\n lines.push(SEPARATOR);\n lines.push(s.dichiarazioneHeader(d.annoImposta));\n lines.push(s.dichiarazioneNota);\n lines.push(\"\");\n\n // Quadro RT — Sezione II (redditi diversi)\n lines.push(s.quadroRTHeader);\n lines.push(` ${s.quadroRTPlusvalenze} ${fmt(rt.plusvalenze)}`);\n lines.push(` ${s.quadroRTMinusvalenze} ${fmt(rt.minusvalenze)}`);\n lines.push(` ${s.quadroRTDifferenza} ${fmt(rt.differenza)}`);\n for (const entry of rt.carryForwardApplied) {\n lines.push(\n ` ${s.quadroRTRiporto(entry.annoOrigine)} ${fmt(entry.importo)}`,\n );\n }\n let rtNLine = ` ${s.quadroRTImponibile} ${fmt(rt.imponibileNetto)}`;\n if (rt.differenza < 0) {\n rtNLine += ` (${s.quadroRTPerdita} ${fmt(Math.abs(rt.differenza))})`;\n }\n if (rt.differenza > 0 && !carryForwardWasProvided) {\n rtNLine += ` ${s.warnNoCarryForwardProvided}`;\n }\n lines.push(rtNLine);\n lines.push(` ${s.quadroRTImposta} ${fmt(rt.imposta)}`);\n if (rt.differenza < 0) {\n const riportato = rt.carryForwardRiportato.reduce(\n (sum, e) => sum + e.importo,\n 0,\n );\n lines.push(\n ` ${s.quadroRTRiportabile} ${fmt(riportato)} ${s.bucketBCarryNote}`,\n );\n }\n lines.push(\"\");\n\n // Quadro RM (redditi da capitale)\n lines.push(s.quadroRMHeader);\n if (rm.capitaleAliquota26.plusvalenze > 0) {\n lines.push(\n ` ${s.quadroRMEtf26} ${fmt(rm.capitaleAliquota26.plusvalenze)}`,\n );\n lines.push(\n ` [RM-A1] ${s.quadroRMImposta} ${fmt(rm.capitaleAliquota26.imposta)}`,\n );\n }\n if (rm.capitaleAliquota125.plusvalenze > 0) {\n lines.push(\n ` ${s.quadroRMBtp} ${fmt(rm.capitaleAliquota125.plusvalenze)}`,\n );\n lines.push(\n ` [RM-A2] ${s.quadroRMImposta} ${fmt(rm.capitaleAliquota125.imposta)}`,\n );\n }\n for (const div of rm.dividendiEsteri) {\n lines.push(\n ` ${s.quadroRMDividendi} — ${div.prodotto} (${div.isin}) ${fmt(div.lordo)}`,\n );\n lines.push(\n ` [RM-D] ${s.quadroRMRitenuta} — ${div.prodotto} (${div.isin}) ${fmt(div.rittenutaEstera)}`,\n );\n }\n for (const c of rm.cedole) {\n lines.push(\n ` ${s.quadroRMCedole} — ${c.prodotto} (${c.isin}) ${fmt(c.importo)}`,\n );\n }\n lines.push(\"\");\n\n lines.push(s.dichiarazioneWarningRow);\n lines.push(s.dichiarazioneDisclaimer);\n\n return lines.join(\"\\n\");\n}\n\nexport function renderReport(\n report: GainsReport,\n s: LocaleStrings,\n carryForwardWasProvided: boolean,\n): string {\n const fmt = (n: number) => formatEUR(n, s.numberLocale);\n const lines: string[] = [];\n\n const hasBuckets = !!(report.bucketA || report.bucketB);\n\n // Header\n lines.push(\n `${s.headerMethod}: ${report.method} | ${s.headerTaxYear}: ${report.taxYear}`,\n );\n lines.push(\"\");\n\n // Pre-table warnings (AVVISO: or WARNING:)\n for (const w of report.warnings) {\n if (w.startsWith(\"AVVISO:\") || w.startsWith(\"WARNING:\")) {\n lines.push(w);\n lines.push(\"\");\n }\n }\n\n // Column headers\n const headerCols = [\n s.headerIsin.padEnd(14),\n s.headerProduct.padEnd(20),\n s.headerQty.padStart(5),\n s.headerBuyDate.padEnd(12),\n s.headerSellDate.padEnd(12),\n s.headerBuyEur.padStart(14),\n s.headerSellEur.padStart(13),\n s.headerGainLoss.padStart(17),\n ];\n if (hasBuckets) {\n headerCols.push(s.headerBucket.padStart(8));\n }\n lines.push(headerCols.join(\" \"));\n\n // Lot rows\n for (const lot of report.lots) {\n const rowCols = [\n lot.isin.padEnd(14),\n lot.product.substring(0, 20).padEnd(20),\n String(lot.quantity).padStart(5),\n lot.buyDate.padEnd(12),\n lot.sellDate.padEnd(12),\n fmt(lot.buyCostEUR).padStart(14),\n fmt(lot.sellProceedsEUR).padStart(13),\n formatGainLoss(lot.gainLossEUR, s.numberLocale).padStart(17),\n ];\n if (hasBuckets) {\n rowCols.push((lot.bucket ?? \"\").padStart(8));\n }\n lines.push(rowCols.join(\" \"));\n }\n\n lines.push(\"\");\n lines.push(SEPARATOR);\n\n // Summary\n lines.push(`${s.summaryPlusvalenze}: ${fmt(report.plusvalenze)} EUR`);\n lines.push(`${s.summaryMinusvalenze}: ${fmt(report.minusvalenze)} EUR`);\n lines.push(`${s.summaryNetResult}: ${fmt(report.netResult)} EUR`);\n lines.push(\"\");\n lines.push(`${s.summaryWarnings}: ${report.warnings.length}`);\n lines.push(`${s.summaryGenerated}: ${report.generatedAt}`);\n lines.push(\"\");\n\n // Bucket A section\n if (report.bucketA && report.bucketA.groups.length > 0) {\n lines.push(SEPARATOR);\n lines.push(s.bucketAHeader);\n lines.push(SEPARATOR);\n for (const g of report.bucketA.groups) {\n const label = g.assetClasses\n .map((ac) => assetClassLabel(ac, s))\n .join(\", \");\n const rateDisplay = (g.taxRate * 100).toFixed(0);\n lines.push(\n `${label}: ${fmt(g.plusvalenze)} EUR → imposta: ${fmt(g.imposta)} EUR (${rateDisplay}%)`,\n );\n }\n lines.push(`${s.bucketATotalTax}: ${fmt(report.bucketA.totalImposta)} EUR`);\n lines.push(\"\");\n }\n\n // Bucket B section\n if (report.bucketB) {\n lines.push(SEPARATOR);\n lines.push(s.bucketBHeader);\n lines.push(SEPARATOR);\n lines.push(`PLUSVALENZE: ${fmt(report.bucketB.plusvalenze)} EUR`);\n lines.push(`MINUSVALENZE: ${fmt(report.bucketB.minusvalenze)} EUR`);\n if (report.bucketB.carryForwardApplied > 0) {\n lines.push(\n `${s.bucketBCarryApplied(0)}: ${fmt(report.bucketB.carryForwardApplied)} EUR`,\n );\n }\n let resultLine = `${s.bucketBResult}: ${fmt(report.bucketB.netResult)} EUR`;\n if (report.bucketB.carryForwardRemaining > 0) {\n resultLine += ` ${s.bucketBCarryNote}`;\n }\n lines.push(resultLine);\n lines.push(\"\");\n }\n\n // Mixed-buckets footnote\n if (hasBuckets) {\n lines.push(s.warnMixedBuckets);\n lines.push(\"\");\n }\n\n if (report.dichiarazione) {\n lines.push(renderDichiarazione(report, s, carryForwardWasProvided));\n } else {\n lines.push(s.disclaimer);\n }\n\n return lines.join(\"\\n\");\n}\n","import * as fs from \"node:fs\";\nimport { DEGIROParser } from \"../../parser/index.js\";\nimport { ParseError } from \"../../errors.js\";\nimport type { LocaleStrings } from \"../../i18n/types.js\";\n\nexport async function runValidate(\n positional: string[],\n flags: Record<string, string | boolean>,\n s: LocaleStrings,\n stdout: NodeJS.WritableStream,\n stderr: NodeJS.WritableStream,\n): Promise<number> {\n const filePath = positional[0];\n if (!filePath) {\n stderr.write(\"Usage: minus-tracker validate <file.csv>\\n\");\n return 2;\n }\n\n let csv: string;\n try {\n csv = fs.readFileSync(filePath, \"utf8\");\n } catch {\n stderr.write(`Cannot read file: ${filePath}\\n`);\n return 1;\n }\n\n const parser = new DEGIROParser();\n let transactions;\n try {\n transactions = parser.parse(csv);\n } catch (err) {\n if (err instanceof ParseError) {\n if (err.code === \"INVALID_CSV\") {\n stderr.write(s.errorInvalidCsv + \"\\n\");\n } else {\n stderr.write(s.errorMissingColumn(err.columnName!) + \"\\n\");\n }\n return 1;\n }\n throw err;\n }\n\n stdout.write(s.validateOk(transactions.length, 0) + \"\\n\");\n\n for (const entry of parser.warningEntries) {\n let reason: string;\n switch (entry.code) {\n case \"MISSING_ISIN\":\n reason = s.warnMissingIsin(entry.row);\n break;\n case \"UNSUPPORTED_CURRENCY\":\n reason = s.warnUnsupportedCurrency(entry.row, entry.currency);\n break;\n case \"NO_ECB_RATE\":\n reason = s.warnNoEcbRate(entry.row, entry.currency, entry.date);\n break;\n case \"QUANTITY_ZERO\":\n reason = s.warnQuantityZero(entry.row);\n break;\n case \"MISSING_ISIN_INCOME\":\n reason = s.warnMissingIsinIncome(entry.row);\n break;\n case \"ORPHAN_WITHHOLDING\":\n reason = s.warnOrphanWithholding(entry.isin, entry.date);\n break;\n }\n stdout.write(reason + \"\\n\");\n }\n\n return 0;\n}\n","import * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport * as os from \"node:os\";\nimport * as https from \"node:https\";\nimport { getActiveSnapshot, type RatesSnapshot } from \"../../rates/index.js\";\nimport type { LocaleStrings } from \"../../i18n/types.js\";\n\nexport function getSnapshotPath(): string {\n const platform = process.platform;\n let configDir: string;\n if (platform === \"win32\") {\n configDir =\n process.env.APPDATA ?? path.join(os.homedir(), \"AppData\", \"Roaming\");\n } else {\n configDir =\n process.env.XDG_CONFIG_HOME ?? path.join(os.homedir(), \".config\");\n }\n return path.join(configDir, \"minus-tracker\", \"ecb-rates.json\");\n}\n\nfunction countMissingBusinessDays(\n startDate: string,\n endDate: string,\n dates: Record<string, number>,\n): number {\n let missing = 0;\n const cursor = new Date(startDate + \"T00:00:00Z\");\n const end = new Date(endDate + \"T00:00:00Z\");\n while (cursor <= end) {\n const day = cursor.getUTCDay();\n const iso = cursor.toISOString().slice(0, 10);\n if (day !== 0 && day !== 6 && dates[iso] === undefined) missing++;\n cursor.setUTCDate(cursor.getUTCDate() + 1);\n }\n return missing;\n}\n\nfunction getCoverage(snapshot: RatesSnapshot): {\n start: string;\n end: string;\n currencies: string;\n gaps: string;\n} {\n let start = \"9999-12-31\";\n let end = \"0000-01-01\";\n const currencies: string[] = [];\n const gapEntries: string[] = [];\n\n for (const [ccy, dates] of Object.entries(snapshot)) {\n currencies.push(ccy);\n const keys = Object.keys(dates).sort();\n for (const d of keys) {\n if (d < start) start = d;\n if (d > end) end = d;\n }\n if (keys.length > 0) {\n const missing = countMissingBusinessDays(\n keys[0],\n keys[keys.length - 1],\n dates,\n );\n if (missing > 0) gapEntries.push(`${ccy}: ${missing}`);\n }\n }\n return {\n start,\n end,\n currencies: currencies.sort().join(\", \"),\n gaps: gapEntries.sort().join(\", \"),\n };\n}\n\nexport async function fetchEcbData(\n currency: string,\n): Promise<Record<string, number>> {\n return new Promise((resolve, reject) => {\n const url = `https://data-api.ecb.europa.eu/service/data/EXR/D.${currency}.EUR.SP00.A?format=csvdata&startPeriod=2019-01-01`;\n https\n .get(url, (res) => {\n let data = \"\";\n res.on(\"data\", (chunk) => (data += chunk));\n res.on(\"end\", () => {\n const rates: Record<string, number> = {};\n const lines = data.split(\"\\n\");\n // ECB SDMX csvdata format:\n // KEY,FREQ,CURRENCY,CURRENCY_DENOM,EXR_TYPE,EXR_SUFFIX,TIME_PERIOD,OBS_VALUE,...\n // DATE is column index 6, rate is column index 7\n for (const line of lines) {\n const parts = line.split(\",\");\n if (parts.length < 8) continue;\n const date = parts[6].trim();\n const rate = parseFloat(parts[7].trim());\n if (/^\\d{4}-\\d{2}-\\d{2}$/.test(date) && !isNaN(rate) && rate > 0) {\n rates[date] = rate;\n }\n }\n resolve(rates);\n });\n res.on(\"error\", reject);\n })\n .on(\"error\", reject);\n });\n}\n\nexport async function updateRates(\n snapshotPath: string,\n stdout: NodeJS.WritableStream,\n stderr: NodeJS.WritableStream,\n s: LocaleStrings,\n): Promise<void> {\n stdout.write(s.ratesUpdateFetching + \"\\n\");\n\n let existing: RatesSnapshot = {};\n try {\n existing = JSON.parse(\n fs.readFileSync(snapshotPath, \"utf8\"),\n ) as RatesSnapshot;\n } catch {\n /* file doesn't exist yet */\n }\n\n let addedCount = 0;\n for (const currency of [\"USD\", \"GBP\", \"CHF\"]) {\n try {\n const newRates = await fetchEcbData(currency);\n const existing_ccy = existing[currency] ?? {};\n let added = 0;\n for (const [date, rate] of Object.entries(newRates)) {\n if (!existing_ccy[date]) added++;\n existing_ccy[date] = rate;\n }\n existing[currency] = existing_ccy;\n addedCount += added;\n } catch {\n stderr.write(`Failed to fetch ${currency} rates\\n`);\n }\n }\n\n fs.mkdirSync(path.dirname(snapshotPath), { recursive: true });\n fs.writeFileSync(\n snapshotPath,\n JSON.stringify(existing, null, 2) + \"\\n\",\n \"utf8\",\n );\n stdout.write(s.ratesUpdateDone(addedCount) + \"\\n\");\n stdout.write(s.ratesSnapshotWritten(snapshotPath) + \"\\n\");\n}\n\nexport async function runRates(\n positional: string[],\n flags: Record<string, string | boolean>,\n s: LocaleStrings,\n stdout: NodeJS.WritableStream,\n stderr: NodeJS.WritableStream,\n): Promise<number> {\n if (flags[\"check\"]) {\n const snapshot = getActiveSnapshot();\n const { start, end, currencies, gaps } = getCoverage(snapshot);\n stdout.write(s.ratesCoverage(start, end, currencies) + \"\\n\");\n stdout.write((gaps ? s.ratesGaps(gaps) : s.ratesGapsNone) + \"\\n\");\n return 0;\n }\n\n if (flags[\"update\"]) {\n await updateRates(getSnapshotPath(), stdout, stderr, s);\n return 0;\n }\n\n stderr.write(\"Usage: minus-tracker rates --check | --update\\n\");\n return 2;\n}\n","import { resolveLocale, saveLocale } from \"../../i18n/settings.js\";\nimport type { LocaleStrings, SupportedLocale } from \"../../i18n/types.js\";\n\nconst SUPPORTED_LOCALES: SupportedLocale[] = [\"it\", \"en\"];\n\nexport async function runConfig(\n positional: string[],\n flags: Record<string, string | boolean>,\n s: LocaleStrings,\n stdout: NodeJS.WritableStream,\n stderr: NodeJS.WritableStream,\n): Promise<number> {\n if (flags[\"lang\"] !== undefined) {\n const lang = flags[\"lang\"] as string;\n if (!SUPPORTED_LOCALES.includes(lang as SupportedLocale)) {\n stderr.write(`Unsupported locale \"${lang}\". Use: it, en\\n`);\n return 2;\n }\n saveLocale(lang as SupportedLocale);\n stdout.write(s.configLangSet(lang as SupportedLocale) + \"\\n\");\n return 0;\n }\n\n if (flags[\"show\"]) {\n const locale = resolveLocale();\n stdout.write(s.configCurrentLang(locale) + \"\\n\");\n return 0;\n }\n\n stderr.write(\"Usage: minus-tracker config --lang <it|en> | --show\\n\");\n return 2;\n}\n","/**\n * stress-test CLI command handler\n * Runs all (or a range of) stress test scenarios against the CLI binary.\n */\n\nimport * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport * as os from \"node:os\";\nimport { fileURLToPath } from \"node:url\";\nimport { spawnSync } from \"node:child_process\";\nimport { generateCsv } from \"../../stress/generator.js\";\nimport { runScenario } from \"../../stress/runner.js\";\nimport { buildReport, formatTable, formatJson } from \"../../stress/reporter.js\";\nimport type { StressManifest } from \"../../stress/generator.js\";\n\nexport async function runStressTest(\n _positionals: string[],\n flags: Record<string, string | boolean>,\n stdout: NodeJS.WritableStream,\n stderr: NodeJS.WritableStream,\n): Promise<number> {\n // 1. Parse --range flag\n const rangeStr = (flags[\"range\"] as string | undefined) ?? \"1-100\";\n const rangeParts = rangeStr.split(\"-\");\n if (rangeParts.length !== 2) {\n stderr.write(\n `Error: --range must be in the form N-M (e.g. 1-100), got: \"${rangeStr}\"\\n`,\n );\n return 2;\n }\n const rangeStart = parseInt(rangeParts[0], 10);\n const rangeEnd = parseInt(rangeParts[1], 10);\n if (\n isNaN(rangeStart) ||\n isNaN(rangeEnd) ||\n rangeStart <= 0 ||\n rangeEnd <= 0 ||\n rangeStart > rangeEnd ||\n rangeStart > 100 ||\n rangeEnd > 100\n ) {\n stderr.write(\n `Error: --range values must be positive integers between 1 and 100 with start ≤ end, got: \"${rangeStr}\"\\n`,\n );\n return 2;\n }\n\n // 2. Resolve manifest path\n const __dirname = path.dirname(fileURLToPath(import.meta.url));\n const manifestPath = path.join(__dirname, \"../data/stress-manifest.json\");\n\n // 3. Load manifest\n let manifest: StressManifest;\n try {\n const raw = fs.readFileSync(manifestPath, \"utf8\");\n manifest = JSON.parse(raw) as StressManifest;\n } catch {\n stderr.write(`Error: could not load stress manifest at ${manifestPath}\\n`);\n return 1;\n }\n\n // 4. Resolve CLI binary\n const cliBin = process.argv[1];\n\n // 5. Create output directory\n let outputDir: string;\n const outputDirFlag = flags[\"output-dir\"] as string | undefined;\n if (outputDirFlag) {\n outputDir = outputDirFlag;\n fs.mkdirSync(outputDir, { recursive: true });\n } else {\n outputDir = fs.mkdtempSync(path.join(os.tmpdir(), \"minus-tracker-stress-\"));\n }\n\n // 8. Run global checks before scenarios\n const ratesCheck = spawnSync(\"node\", [cliBin, \"rates\", \"--check\"], {\n encoding: \"utf8\",\n timeout: 15000,\n });\n if ((ratesCheck.status ?? 1) !== 0) {\n stderr.write(\"Warning: rates --check failed; ECB rates may be stale.\\n\");\n }\n\n const configShow = spawnSync(\"node\", [cliBin, \"config\", \"--show\"], {\n encoding: \"utf8\",\n timeout: 15000,\n });\n if ((configShow.status ?? 1) !== 0) {\n stderr.write(\"Warning: config --show failed.\\n\");\n }\n\n // 6. Filter scenarios by range\n const scenarios = manifest.scenarios.filter((s) => {\n const id = parseInt(s.id, 10);\n return id >= rangeStart && id <= rangeEnd;\n });\n\n // 7. Run each scenario\n const results = [];\n for (const scenario of scenarios) {\n const csv = generateCsv(scenario);\n const result = runScenario(scenario, csv, outputDir, cliBin);\n results.push(result);\n stdout.write(\n ` [${result.pass ? \"✓\" : \"✗\"}] ${scenario.id} ${scenario.slug}\\n`,\n );\n }\n\n // 9. Build and output report\n const report = buildReport(results, rangeStart, rangeEnd);\n if (flags[\"json\"]) {\n stdout.write(formatJson(report) + \"\\n\");\n } else {\n stdout.write(formatTable(report) + \"\\n\");\n }\n\n // 10. Cleanup unless --keep\n if (!flags[\"keep\"]) {\n fs.rmSync(outputDir, { recursive: true, force: true });\n }\n\n // 11. Return exit code\n return report.failed === 0 ? 0 : 1;\n}\n","/**\n * DEGIRO CSV Generator for stress testing\n * Converts manifest scenarios into DEGIRO-format CSV strings.\n */\n\n/**\n * A single transaction from the manifest\n */\nexport interface ManifestTransaction {\n date: string; // YYYY-MM-DD\n product: string;\n isin: string;\n qty: number; // positive = BUY, negative = SELL\n price: number;\n currency: string; // EUR | USD | GBP | CHF\n fee: number; // absolute EUR fee\n}\n\n/**\n * Expected outcomes for a scenario\n */\nexport interface ManifestExpect {\n calc_exit: number;\n fifo_exit: number;\n json_exit: number;\n en_exit: number;\n validate_exit: number;\n warning_count: number;\n}\n\n/**\n * A single test scenario\n */\nexport interface ManifestScenario {\n id: string;\n category: string;\n slug: string;\n description: string;\n transactions: ManifestTransaction[];\n expect: ManifestExpect;\n special?: string;\n}\n\n/**\n * The full stress test manifest\n */\nexport interface StressManifest {\n version: string;\n ecb_reference_dates: Record<string, unknown>;\n scenarios: ManifestScenario[];\n}\n\n/**\n * Convert a date from YYYY-MM-DD to DD-MM-YYYY\n */\nfunction formatDate(isoDate: string): string {\n const [yyyy, mm, dd] = isoDate.split(\"-\");\n return `${dd}-${mm}-${yyyy}`;\n}\n\n/**\n * Generate a DEGIRO-format CSV string for a given scenario.\n *\n * DEGIRO CSV format (18 columns, exact order):\n * Date,Time,Product,ISIN,Exchange,Execution centre,Quantity,Price,Local value,\n * Local value currency,Value,Value currency,Exchange rate,Transaction costs,\n * Transaction costs currency,Total,Total currency,Order ID\n *\n * Returns the CSV content as a string (header + data rows, joined by \\n)\n */\nexport function generateCsv(scenario: ManifestScenario): string {\n // Handle special cases\n if (scenario.special === \"invalid_csv\") {\n // Return garbage bytes (invalid CSV)\n return \"NOT_A_CSV\\x00\\x01\\x02\\ngarbage data\";\n }\n\n // For normal and column-removal cases, build the header first\n const allColumns = [\n \"Date\",\n \"Time\",\n \"Product\",\n \"ISIN\",\n \"Exchange\",\n \"Execution centre\",\n \"Quantity\",\n \"Price\",\n \"Local value\",\n \"Local value currency\",\n \"Value\",\n \"Value currency\",\n \"Exchange rate\",\n \"Transaction costs\",\n \"Transaction costs currency\",\n \"Total\",\n \"Total currency\",\n \"Order ID\",\n ];\n\n // Remove columns based on special field\n let columns = allColumns;\n if (scenario.special === \"missing_col_isin\") {\n columns = columns.filter((c) => c !== \"ISIN\");\n } else if (scenario.special === \"missing_col_quantity\") {\n columns = columns.filter((c) => c !== \"Quantity\");\n } else if (scenario.special === \"missing_col_price\") {\n columns = columns.filter((c) => c !== \"Price\");\n }\n\n // Helper to build a CSV row, omitting columns as needed\n const buildRow = (\n values: Record<string, string>,\n includeColumns: string[],\n ): string => {\n return includeColumns.map((col) => values[col] ?? \"\").join(\",\");\n };\n\n // Build header\n const header = columns.join(\",\");\n const rows = [header];\n\n // Build data rows\n for (let rowIndex = 0; rowIndex < scenario.transactions.length; rowIndex++) {\n const tx = scenario.transactions[rowIndex];\n\n // Calculate local value: -qty * price\n // For BUY (qty > 0): localValue is negative (money out)\n // For SELL (qty < 0): localValue is positive (money in)\n const localValue = -tx.qty * tx.price;\n const total = localValue - tx.fee;\n\n const rowValues: Record<string, string> = {\n Date: formatDate(tx.date),\n Time: \"09:00\",\n Product: tx.product,\n ISIN: tx.isin,\n Exchange: \"XEUR\",\n \"Execution centre\": \"XEUR\",\n Quantity: String(tx.qty),\n Price: tx.price.toFixed(2),\n \"Local value\": localValue.toFixed(2),\n \"Local value currency\": tx.currency,\n Value: localValue.toFixed(2),\n \"Value currency\": \"EUR\",\n \"Exchange rate\": \"1\",\n \"Transaction costs\": (-Math.abs(tx.fee)).toFixed(2),\n \"Transaction costs currency\": \"EUR\",\n Total: total.toFixed(2),\n \"Total currency\": \"EUR\",\n \"Order ID\": `stress-${scenario.id}-${rowIndex}`,\n };\n\n const row = buildRow(rowValues, columns);\n rows.push(row);\n }\n\n return rows.join(\"\\n\");\n}\n","/**\n * CLI runner for minus-tracker stress test scenarios\n * Executes CLI commands against generated CSV files and collects results.\n */\n\nimport { spawnSync } from \"node:child_process\";\nimport { writeFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport type { ManifestScenario } from \"./generator.js\";\n\n/**\n * Result of running a single CLI command\n */\nexport interface CommandResult {\n cmd: string;\n exitCode: number;\n stdout: string;\n stderr: string;\n pass: boolean;\n failure?: string;\n}\n\n/**\n * Result of running all commands for a scenario\n */\nexport interface ScenarioResult {\n id: string;\n category: string;\n slug: string;\n description: string;\n csvFile: string;\n results: CommandResult[];\n pass: boolean;\n warningCheckPass: boolean;\n expectedWarningCount: number;\n actualWarningCount: number;\n}\n\n/**\n * Extract the warning count from calc output.\n * The English renderer emits \"WARNINGS: N\" and the Italian renderer emits \"AVVERTENZE: N\".\n * We parse that numeric value directly rather than counting matching lines.\n */\nfunction countWarnings(text: string): number {\n if (!text) return 0;\n const match = text.match(/(?:warnings|avvertenze)\\s*:\\s*(\\d+)/i);\n return match ? parseInt(match[1], 10) : 0;\n}\n\n/**\n * Check if a string is valid JSON with required keys\n */\nfunction isValidJson(jsonStr: string): boolean {\n try {\n const obj = JSON.parse(jsonStr);\n return (\n typeof obj === \"object\" &&\n obj !== null &&\n \"plusvalenze\" in obj &&\n \"minusvalenze\" in obj &&\n \"lots\" in obj\n );\n } catch {\n return false;\n }\n}\n\n/**\n * Run a single CLI command and return its result\n */\nfunction runCommand(\n cliBin: string,\n args: string[],\n cmdLabel: string,\n expectedExitCode: number,\n outputShapeCheck: (\n stdout: string,\n stderr: string,\n exitCode: number,\n ) => { valid: boolean; reason?: string },\n): CommandResult {\n // Determine how to invoke the CLI\n const isJsFile = cliBin.endsWith(\".js\");\n let result;\n\n if (isJsFile) {\n // Run: node <cliBin> <args>\n result = spawnSync(\"node\", [cliBin, ...args], {\n encoding: \"utf8\",\n timeout: 15000,\n env: { ...process.env, MINUS_TRACKER_LANG: undefined },\n });\n } else {\n // Run: <cliBin> <args> (directly executable)\n result = spawnSync(cliBin, args, {\n encoding: \"utf8\",\n timeout: 15000,\n env: { ...process.env, MINUS_TRACKER_LANG: undefined },\n });\n }\n\n const exitCode = result.status ?? 1; // null status (timeout/error) → treat as 1\n const stdout = result.stdout || \"\";\n const stderr = result.stderr || \"\";\n\n // Check exit code match\n const exitCodeMatch = exitCode === expectedExitCode;\n\n // Check output shape\n const shapeCheck = outputShapeCheck(stdout, stderr, exitCode);\n\n let pass = exitCodeMatch && shapeCheck.valid;\n let failure: string | undefined;\n\n if (!exitCodeMatch) {\n failure = `exit code mismatch: expected ${expectedExitCode}, got ${exitCode}`;\n } else if (!shapeCheck.valid) {\n failure = shapeCheck.reason;\n }\n\n return {\n cmd: cmdLabel,\n exitCode,\n stdout,\n stderr,\n pass,\n failure,\n };\n}\n\n/**\n * Run all CLI commands for a scenario and return the result\n */\nexport function runScenario(\n scenario: ManifestScenario,\n csvContent: string,\n outputDir: string,\n cliBin: string,\n): ScenarioResult {\n // Write CSV to temp file\n const csvFileName = `${scenario.id}-${scenario.slug}.csv`;\n const csvFile = join(outputDir, csvFileName);\n writeFileSync(csvFile, csvContent, \"utf8\");\n\n const results: CommandResult[] = [];\n\n // Run calc command (LIFO)\n results.push(\n runCommand(\n cliBin,\n [\"calc\", csvFile],\n \"calc\",\n scenario.expect.calc_exit,\n (stdout) => {\n if (scenario.expect.calc_exit === 0) {\n const hasPlus = /plusvalenze|plusvalues/i.test(stdout);\n return hasPlus\n ? { valid: true }\n : {\n valid: false,\n reason: \"calc output missing plusvalenze/plusvalues (exit 0)\",\n };\n }\n return { valid: true };\n },\n ),\n );\n\n // Run calc --method FIFO\n results.push(\n runCommand(\n cliBin,\n [\"calc\", \"--method\", \"FIFO\", csvFile],\n \"calc --method FIFO\",\n scenario.expect.fifo_exit,\n (stdout) => {\n if (scenario.expect.fifo_exit === 0) {\n const hasPlus = /plusvalenze|plusvalues/i.test(stdout);\n return hasPlus\n ? { valid: true }\n : {\n valid: false,\n reason:\n \"calc --method FIFO output missing plusvalenze/plusvalues (exit 0)\",\n };\n }\n return { valid: true };\n },\n ),\n );\n\n // Run calc --json\n results.push(\n runCommand(\n cliBin,\n [\"calc\", \"--json\", csvFile],\n \"calc --json\",\n scenario.expect.json_exit,\n (stdout) => {\n if (scenario.expect.json_exit === 0) {\n const isValid = isValidJson(stdout);\n return isValid\n ? { valid: true }\n : {\n valid: false,\n reason:\n \"calc --json output is not valid JSON with required keys (exit 0)\",\n };\n }\n return { valid: true };\n },\n ),\n );\n\n // Run calc --lang en\n results.push(\n runCommand(\n cliBin,\n [\"calc\", \"--lang\", \"en\", csvFile],\n \"calc --lang en\",\n scenario.expect.en_exit,\n (stdout) => {\n if (scenario.expect.en_exit === 0) {\n const nonEmpty = stdout.trim().length > 0;\n return nonEmpty\n ? { valid: true }\n : {\n valid: false,\n reason: \"calc --lang en output is empty (exit 0)\",\n };\n }\n return { valid: true };\n },\n ),\n );\n\n // Run validate\n results.push(\n runCommand(\n cliBin,\n [\"validate\", csvFile],\n \"validate\",\n scenario.expect.validate_exit,\n (stdout) => {\n if (scenario.expect.validate_exit !== 0) return { valid: true };\n const nonEmpty = stdout.trim().length > 0;\n return nonEmpty\n ? { valid: true }\n : {\n valid: false,\n reason: \"validate output is empty\",\n };\n },\n ),\n );\n\n // Count warnings using English locale calc output (immune to locale config)\n // English output emits \"WARNINGS: N\" which matches the /warn/i pattern reliably\n const enCalcResult = results[3]; // calc --lang en is always index 3\n const actualWarningCount = countWarnings(enCalcResult.stdout);\n const warningCheckPass = actualWarningCount >= scenario.expect.warning_count;\n\n // Determine overall pass\n const pass = results.every((r) => r.pass) && warningCheckPass;\n\n return {\n id: scenario.id,\n category: scenario.category,\n slug: scenario.slug,\n description: scenario.description,\n csvFile,\n results,\n pass,\n warningCheckPass,\n expectedWarningCount: scenario.expect.warning_count,\n actualWarningCount,\n };\n}\n","/**\n * Reporter for minus-tracker stress test suite.\n * Formats stress test results into human-readable table + summary.\n */\n\nimport type { ScenarioResult } from \"./runner.js\";\n\n/**\n * Aggregated stress test report\n */\nexport interface StressReport {\n totalScenarios: number;\n passed: number;\n failed: number;\n results: ScenarioResult[];\n rangeStart: number; // 1-based\n rangeEnd: number;\n}\n\n/**\n * Aggregates a list of ScenarioResult into a StressReport\n */\nexport function buildReport(\n results: ScenarioResult[],\n rangeStart: number,\n rangeEnd: number,\n): StressReport {\n const passed = results.filter((r) => r.pass).length;\n const failed = results.length - passed;\n\n return {\n totalScenarios: results.length,\n passed,\n failed,\n results,\n rangeStart,\n rangeEnd,\n };\n}\n\n/**\n * Format a ScenarioResult as a JSON string\n */\nexport function formatJson(report: StressReport): string {\n return JSON.stringify(report, null, 2);\n}\n\n/**\n * Format a StressReport as a human-readable ASCII table\n */\nexport function formatTable(report: StressReport): string {\n const lines: string[] = [];\n\n // Header\n lines.push(\"STRESS TEST — minus-tracker\");\n lines.push(\n `Scenari: ${report.totalScenarios} | Passati: ${report.passed} | Falliti: ${report.failed}`,\n );\n lines.push(\"\");\n\n // Column header\n const idCol = \"ID\";\n const categoryCol = \"CATEGORIA\";\n const descCol = \"DESCRIZIONE\";\n const resultCol = \"ESITO\";\n\n lines.push(formatRow(idCol, categoryCol, descCol, resultCol));\n\n // Rows for each scenario\n for (const result of report.results) {\n const idStr = result.id.padStart(3, \"0\");\n const status = result.pass ? \"✓ PASS\" : \"✗ FAIL\";\n\n lines.push(formatRow(idStr, result.category, result.description, status));\n\n // If failed, add failure details\n if (!result.pass) {\n for (const cmdResult of result.results) {\n if (!cmdResult.pass) {\n if (cmdResult.failure) {\n let failureText = \"\";\n\n // Check for exit code mismatch pattern\n if (cmdResult.failure.includes(\"exit code mismatch\")) {\n const match = cmdResult.failure.match(\n /expected (\\d+), got (\\d+)/,\n );\n if (match) {\n failureText = `atteso exit ${match[1]}, ottenuto exit ${match[2]}`;\n }\n } else {\n // Output shape check failed\n failureText = `output shape non valido — ${cmdResult.failure}`;\n }\n\n lines.push(` → ${cmdResult.cmd}: ${failureText}`);\n }\n }\n }\n\n // Check warning count\n if (!result.warningCheckPass) {\n lines.push(\n ` → avvertenze: attese ≥${result.expectedWarningCount}, trovate ${result.actualWarningCount}`,\n );\n }\n }\n }\n\n // Summary footer\n lines.push(\"\");\n lines.push(`PASSATI: ${report.passed}`);\n lines.push(`FALLITI: ${report.failed}`);\n lines.push(\"\");\n\n return lines.join(\"\\n\");\n}\n\n/**\n * Format a single table row with fixed column widths\n */\nfunction formatRow(\n id: string,\n category: string,\n description: string,\n esito: string,\n): string {\n // Column widths: ID=4, CATEGORIA=22, DESCRIZIONE=40, ESITO=8\n const idWidth = 4;\n const categoryWidth = 22;\n const descWidth = 40;\n const esitoWidth = 8;\n\n // Pad/truncate columns\n const idCol = id.padEnd(idWidth);\n const categoryCol = truncateOrPad(category, categoryWidth);\n const descCol = truncateOrPad(description, descWidth);\n const esitoCol = esito.padEnd(esitoWidth);\n\n return `${idCol} ${categoryCol} ${descCol} ${esitoCol}`;\n}\n\n/**\n * Truncate a string to max width with \"..\" suffix, or pad to width\n */\nfunction truncateOrPad(str: string, maxWidth: number): string {\n if (str.length > maxWidth) {\n return str.slice(0, maxWidth - 2) + \"..\";\n }\n return str.padEnd(maxWidth);\n}\n","import * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport { DEGIROParser } from \"../../parser/index.js\";\nimport { Classifier } from \"../../classifier/index.js\";\nimport { ParseError } from \"../../errors.js\";\nimport type { LocaleStrings } from \"../../i18n/types.js\";\nimport type { ClassificationMap } from \"../../types.js\";\n\nexport async function runClassify(\n positional: string[],\n flags: Record<string, string | boolean>,\n s: LocaleStrings,\n stdout: NodeJS.WritableStream,\n stderr: NodeJS.WritableStream,\n): Promise<number> {\n const offline = Boolean(flags[\"offline\"]);\n\n // TTY check — FIRST, before any file I/O\n if (!process.stdin.isTTY && !offline) {\n stderr.write(s.classifyNonTtyError + \"\\n\");\n return 2;\n }\n\n const csvPath = positional[0];\n if (!csvPath) {\n stderr.write(\"Usage: minus-tracker classify [--offline] <file.csv>\\n\");\n return 2;\n }\n\n let csv: string;\n try {\n csv = fs.readFileSync(csvPath, \"utf8\");\n } catch {\n stderr.write(`Cannot read file: ${csvPath}\\n`);\n return 1;\n }\n\n const parser = new DEGIROParser();\n let transactions;\n try {\n transactions = parser.parse(csv);\n } catch (err) {\n if (err instanceof ParseError) {\n if (err.code === \"INVALID_CSV\") {\n stderr.write(s.errorInvalidCsv + \"\\n\");\n } else {\n stderr.write(s.errorMissingColumn(err.columnName!) + \"\\n\");\n }\n return 1;\n }\n throw err;\n }\n\n // Derive sidecar path: replace .csv extension with .classify.json\n const base = csvPath.replace(/\\.csv$/i, \"\");\n const sidecarPath = base + \".classify.json\";\n\n if (offline) {\n // Offline mode: skip OpenFIGI; write stub user-confirmed entries for each unique ISIN\n stdout.write(s.classifyOfflineWarning + \"\\n\");\n\n const isinToProduct = new Map<string, string>();\n for (const tx of transactions) {\n if (tx.isin && !isinToProduct.has(tx.isin)) {\n isinToProduct.set(tx.isin, tx.product);\n }\n }\n\n const classifications: ClassificationMap = {};\n for (const [isin, product] of isinToProduct.entries()) {\n classifications[isin] = {\n product,\n assetClass: \"ETF\",\n bucketGain: \"A\",\n bucketLoss: \"B\",\n taxRate: 0.26,\n whiteListed: null,\n confirmedByUser: true,\n source: \"user\",\n };\n }\n\n const sidecarContent = JSON.stringify(\n {\n version: 1,\n generatedAt: new Date().toISOString(),\n classifications,\n warnings: [],\n },\n null,\n 2,\n );\n\n try {\n fs.writeFileSync(sidecarPath, sidecarContent, \"utf-8\");\n } catch {\n stderr.write(`Cannot write sidecar: ${sidecarPath}\\n`);\n return 1;\n }\n\n stdout.write(s.classifyWritten(sidecarPath) + \"\\n\");\n return 0;\n }\n\n // Online interactive mode\n const classifier = new Classifier({ interactive: true });\n\n if (fs.existsSync(sidecarPath)) {\n const existingMap = await classifier.load(sidecarPath);\n const confirmedCount = Object.values(existingMap).filter(\n (e) => e.confirmedByUser,\n ).length;\n stdout.write(s.classifyMergePrompt(confirmedCount));\n\n const answer = await readLine(process.stdin);\n if (answer.trim().toLowerCase() === \"n\") {\n return 0;\n }\n }\n\n await classifier.classify(transactions, sidecarPath);\n stdout.write(s.classifyWritten(path.resolve(sidecarPath)) + \"\\n\");\n return 0;\n}\n\nfunction readLine(stream: NodeJS.ReadableStream): Promise<string> {\n return new Promise((resolve) => {\n let buf = \"\";\n const onData = (chunk: Buffer | string) => {\n buf += chunk.toString();\n const nl = buf.indexOf(\"\\n\");\n if (nl !== -1) {\n stream.removeListener(\"data\", onData);\n resolve(buf.slice(0, nl));\n }\n };\n stream.on(\"data\", onData);\n });\n}\n"],"mappings":"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","names":["path","path","fs","path","os","path","fs","get","isoDate","path","roundHalfUp","fs","resolve","fs","fs","path","os","https","resolve","SUPPORTED_LOCALES","fs","path","os","fileURLToPath","spawnSync","writeFileSync","join","fileURLToPath","spawnSync","fs","path","resolve"]}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@gabrielerandelli/minus-tracker",
3
- "version": "0.8.0",
4
- "description": "Italian capital-gains/loss calculator for the Regime Dichiarativo",
3
+ "version": "0.8.1",
4
+ "description": "Italian capital-gains/loss calculator (LIFO/FIFO) with CLI and MCP server for AI agents — Regime Dichiarativo",
5
5
  "type": "module",
6
6
  "exports": {
7
7
  ".": {
@@ -52,7 +52,14 @@
52
52
  "csv",
53
53
  "lifo",
54
54
  "fifo",
55
- "typescript"
55
+ "typescript",
56
+ "mcp",
57
+ "mcp-server",
58
+ "model-context-protocol",
59
+ "ai-agent",
60
+ "llm-tools",
61
+ "claude",
62
+ "tax-calculator"
56
63
  ],
57
64
  "homepage": "https://github.com/gabrielerandelli/minus-tracker#readme",
58
65
  "bugs": {