seo-testing-tool 1.0.0
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/LICENSE +21 -0
- package/README.md +228 -0
- package/dist/auth/GoogleOAuthService.d.ts +31 -0
- package/dist/auth/GoogleOAuthService.d.ts.map +1 -0
- package/dist/auth/GoogleOAuthService.js +69 -0
- package/dist/auth/GoogleOAuthService.js.map +1 -0
- package/dist/auth/TokenManager.d.ts +56 -0
- package/dist/auth/TokenManager.d.ts.map +1 -0
- package/dist/auth/TokenManager.js +190 -0
- package/dist/auth/TokenManager.js.map +1 -0
- package/dist/cli/commands.d.ts +36 -0
- package/dist/cli/commands.d.ts.map +1 -0
- package/dist/cli/commands.js +471 -0
- package/dist/cli/commands.js.map +1 -0
- package/dist/cli/formatters.d.ts +24 -0
- package/dist/cli/formatters.d.ts.map +1 -0
- package/dist/cli/formatters.js +175 -0
- package/dist/cli/formatters.js.map +1 -0
- package/dist/cli.d.ts +15 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +62 -0
- package/dist/cli.js.map +1 -0
- package/dist/config/AnalysisConfig.d.ts +13 -0
- package/dist/config/AnalysisConfig.d.ts.map +1 -0
- package/dist/config/AnalysisConfig.js +10 -0
- package/dist/config/AnalysisConfig.js.map +1 -0
- package/dist/config/env.d.ts +30 -0
- package/dist/config/env.d.ts.map +1 -0
- package/dist/config/env.js +77 -0
- package/dist/config/env.js.map +1 -0
- package/dist/database/DatabaseService.d.ts +106 -0
- package/dist/database/DatabaseService.d.ts.map +1 -0
- package/dist/database/DatabaseService.js +180 -0
- package/dist/database/DatabaseService.js.map +1 -0
- package/dist/database/TimeSeriesService.d.ts +53 -0
- package/dist/database/TimeSeriesService.d.ts.map +1 -0
- package/dist/database/TimeSeriesService.js +122 -0
- package/dist/database/TimeSeriesService.js.map +1 -0
- package/dist/database/db.d.ts +20 -0
- package/dist/database/db.d.ts.map +1 -0
- package/dist/database/db.js +60 -0
- package/dist/database/db.js.map +1 -0
- package/dist/database/schema.d.ts +687 -0
- package/dist/database/schema.d.ts.map +1 -0
- package/dist/database/schema.js +62 -0
- package/dist/database/schema.js.map +1 -0
- package/dist/demo.d.ts +13 -0
- package/dist/demo.d.ts.map +1 -0
- package/dist/demo.js +149 -0
- package/dist/demo.js.map +1 -0
- package/dist/gsc/GSCDataFetcher.d.ts +100 -0
- package/dist/gsc/GSCDataFetcher.d.ts.map +1 -0
- package/dist/gsc/GSCDataFetcher.js +398 -0
- package/dist/gsc/GSCDataFetcher.js.map +1 -0
- package/dist/gsc/GSCPermissionService.d.ts +20 -0
- package/dist/gsc/GSCPermissionService.d.ts.map +1 -0
- package/dist/gsc/GSCPermissionService.js +84 -0
- package/dist/gsc/GSCPermissionService.js.map +1 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +41 -0
- package/dist/index.js.map +1 -0
- package/dist/notifications/NotificationService.d.ts +64 -0
- package/dist/notifications/NotificationService.d.ts.map +1 -0
- package/dist/notifications/NotificationService.js +115 -0
- package/dist/notifications/NotificationService.js.map +1 -0
- package/dist/orchestrator/SEOExperimentOrchestrator.d.ts +69 -0
- package/dist/orchestrator/SEOExperimentOrchestrator.d.ts.map +1 -0
- package/dist/orchestrator/SEOExperimentOrchestrator.js +199 -0
- package/dist/orchestrator/SEOExperimentOrchestrator.js.map +1 -0
- package/dist/services/ExportService.d.ts +52 -0
- package/dist/services/ExportService.d.ts.map +1 -0
- package/dist/services/ExportService.js +238 -0
- package/dist/services/ExportService.js.map +1 -0
- package/dist/smoke-test.d.ts +10 -0
- package/dist/smoke-test.d.ts.map +1 -0
- package/dist/smoke-test.js +73 -0
- package/dist/smoke-test.js.map +1 -0
- package/dist/stats/StatisticalEngine.d.ts +48 -0
- package/dist/stats/StatisticalEngine.d.ts.map +1 -0
- package/dist/stats/StatisticalEngine.js +205 -0
- package/dist/stats/StatisticalEngine.js.map +1 -0
- package/dist/stats/TDistribution.d.ts +28 -0
- package/dist/stats/TDistribution.d.ts.map +1 -0
- package/dist/stats/TDistribution.js +120 -0
- package/dist/stats/TDistribution.js.map +1 -0
- package/drizzle/0000_hot_whiplash.sql +51 -0
- package/drizzle/0001_open_photon.sql +9 -0
- package/drizzle/0002_faulty_the_watchers.sql +1 -0
- package/drizzle/meta/0000_snapshot.json +360 -0
- package/drizzle/meta/0001_snapshot.json +428 -0
- package/drizzle/meta/0002_snapshot.json +420 -0
- package/drizzle/meta/_journal.json +27 -0
- package/package.json +89 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 seo-testing-tool contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
# SEO Testing Tool v1.0.0
|
|
2
|
+
|
|
3
|
+
Strumento CLI per esperimenti SEO con analisi statistica (Welch's t-test), integrazione Google Search Console. Database SQLite locale (zero configurazione).
|
|
4
|
+
|
|
5
|
+
## Visione
|
|
6
|
+
|
|
7
|
+
Rispondere in modo affidabile alla domanda:
|
|
8
|
+
**"Questa modifica SEO ha probabilmente migliorato o peggiorato le performance su Google?"**
|
|
9
|
+
|
|
10
|
+
Il tool non promette certezze, ma supporta decisioni informate attraverso analisi statistica rigorosa.
|
|
11
|
+
|
|
12
|
+
## Prerequisiti
|
|
13
|
+
|
|
14
|
+
- Node.js >= 18
|
|
15
|
+
- Credenziali OAuth2 Google (per integrazione GSC)
|
|
16
|
+
|
|
17
|
+
> Il database SQLite viene creato automaticamente in `~/.seo-tool/data.db` al primo avvio. Non serve installare nulla.
|
|
18
|
+
|
|
19
|
+
## Installazione
|
|
20
|
+
|
|
21
|
+
### Da npm (utenti)
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
npm install -g seo-testing-tool
|
|
25
|
+
seo-tool --help
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
> Richiede Node.js >= 18. Su alcune piattaforme `better-sqlite3` potrebbe richiedere build tools (python, make).
|
|
29
|
+
|
|
30
|
+
### Da sorgente (sviluppatori)
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
git clone https://github.com/svilupp0/SEO-Testing-Tool.git
|
|
34
|
+
cd SEO-Testing-Tool
|
|
35
|
+
npm install
|
|
36
|
+
cp .env.example .env
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
### Variabili d'ambiente (.env)
|
|
40
|
+
|
|
41
|
+
```env
|
|
42
|
+
# Opzionale: default ~/.seo-tool/data.db
|
|
43
|
+
# DATABASE_URL=file:/percorso/personalizzato/data.db
|
|
44
|
+
GOOGLE_CLIENT_ID=xxx.apps.googleusercontent.com
|
|
45
|
+
GOOGLE_CLIENT_SECRET=xxx
|
|
46
|
+
GOOGLE_REDIRECT_URI=http://localhost:3000/auth/callback
|
|
47
|
+
USER_ID=default-user
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### Setup database
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
# Crea le tabelle nel database SQLite locale (via Drizzle)
|
|
54
|
+
npm run db:push
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### Verifica connessione DB
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
npm run smoke-test
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## Quick Start (Demo Mode)
|
|
64
|
+
|
|
65
|
+
Vuoi provare il tool senza configurare Google Search Console? La modalità demo genera due esperimenti con **70 giorni di metriche simulate** (rumore statistico realistico via Box-Muller):
|
|
66
|
+
|
|
67
|
+
- **Esperimento Positivo** — incremento click del +46% nel periodo post (p-value < 0.01)
|
|
68
|
+
- **Esperimento Neutro** — nessuna differenza significativa tra pre e post
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
# Genera i dati demo nel database locale
|
|
72
|
+
npm run demo
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
Una volta completato, esplora i risultati:
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
# Elenca tutti gli esperimenti
|
|
79
|
+
seo-tool list
|
|
80
|
+
|
|
81
|
+
# Visualizza dettaglio con grafico ASCII e analisi statistica
|
|
82
|
+
seo-tool status <ID>
|
|
83
|
+
|
|
84
|
+
# Esporta le metriche in Excel o CSV
|
|
85
|
+
seo-tool export <ID>
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
> L'ID viene mostrato al termine di `npm run demo`. Puoi usare anche solo i primi 8 caratteri.
|
|
89
|
+
|
|
90
|
+
## Comandi CLI
|
|
91
|
+
|
|
92
|
+
| Comando | Descrizione |
|
|
93
|
+
|---------|-------------|
|
|
94
|
+
| `seo-tool add` | Crea un nuovo test SEO (prompt interattivi) |
|
|
95
|
+
| `seo-tool list` | Mostra tabella con tutti i test |
|
|
96
|
+
| `seo-tool status <id>` | Dettaglio test con analisi statistica e grafico ASCII |
|
|
97
|
+
| `seo-tool run` | Sincronizza tutti i test attivi (fetch GSC + analisi) |
|
|
98
|
+
| `seo-tool export <id>` | Esporta metriche in CSV |
|
|
99
|
+
| `seo-tool delete <id>` | Elimina un test (con conferma interattiva) |
|
|
100
|
+
|
|
101
|
+
Tutti i comandi che accettano `<id>` supportano **ID parziali** (es. `seo-tool status abcd`).
|
|
102
|
+
|
|
103
|
+
### Esempi
|
|
104
|
+
|
|
105
|
+
```bash
|
|
106
|
+
# Crea un nuovo test
|
|
107
|
+
seo-tool add
|
|
108
|
+
|
|
109
|
+
# Elenca tutti i test
|
|
110
|
+
seo-tool list
|
|
111
|
+
|
|
112
|
+
# Visualizza dettaglio con grafico ASCII
|
|
113
|
+
seo-tool status abcd1234
|
|
114
|
+
|
|
115
|
+
# Sincronizza dati da GSC
|
|
116
|
+
seo-tool run
|
|
117
|
+
|
|
118
|
+
# Esporta in CSV
|
|
119
|
+
seo-tool export abcd1234
|
|
120
|
+
|
|
121
|
+
# Elimina un test (chiede conferma)
|
|
122
|
+
seo-tool delete abcd1234
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
## Sviluppo
|
|
126
|
+
|
|
127
|
+
```bash
|
|
128
|
+
# Esegui comandi senza build (sviluppo)
|
|
129
|
+
npx tsx src/cli.ts <comando>
|
|
130
|
+
|
|
131
|
+
# Lancia test
|
|
132
|
+
npm test
|
|
133
|
+
|
|
134
|
+
# Test con UI
|
|
135
|
+
npm run test:ui
|
|
136
|
+
|
|
137
|
+
# Test con coverage
|
|
138
|
+
npm run test:coverage
|
|
139
|
+
|
|
140
|
+
# Lint
|
|
141
|
+
npm run lint
|
|
142
|
+
|
|
143
|
+
# Format
|
|
144
|
+
npm run format
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
## Deploy su Railway (opzionale)
|
|
148
|
+
|
|
149
|
+
Il progetto include configurazione per Railway con cron job notturno e database SQLite embedded.
|
|
150
|
+
|
|
151
|
+
### Setup Railway
|
|
152
|
+
|
|
153
|
+
1. Crea un nuovo progetto su [Railway](https://railway.app)
|
|
154
|
+
2. Collega il repository Git
|
|
155
|
+
3. Railway rileverà automaticamente `railway.json` e `Dockerfile`
|
|
156
|
+
4. Configura le variabili d'ambiente nel pannello Railway (credenziali Google, ecc.)
|
|
157
|
+
|
|
158
|
+
### Cron Job
|
|
159
|
+
|
|
160
|
+
Il cron job (`railway.json`) esegue `npm run cli:run` ogni notte alle **03:00 UTC**:
|
|
161
|
+
- Recupera nuovi dati da Google Search Console
|
|
162
|
+
- Esegue analisi statistica (Welch's t-test)
|
|
163
|
+
- Invia notifiche se i risultati sono statisticamente significativi
|
|
164
|
+
|
|
165
|
+
### Build manuale Docker
|
|
166
|
+
|
|
167
|
+
```bash
|
|
168
|
+
# Compila TypeScript
|
|
169
|
+
npm run build
|
|
170
|
+
|
|
171
|
+
# Build immagine
|
|
172
|
+
docker build -t seo-testing-tool .
|
|
173
|
+
|
|
174
|
+
# Run
|
|
175
|
+
docker run --env-file .env seo-testing-tool
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
## Architettura
|
|
179
|
+
|
|
180
|
+
```
|
|
181
|
+
src/
|
|
182
|
+
cli.ts Entry point CLI (commander)
|
|
183
|
+
cli/commands.ts Comandi: add, list, status, run, export, delete
|
|
184
|
+
cli/formatters.ts Colori, tabelle, grafici ASCII
|
|
185
|
+
demo.ts Script per generazione dati demo
|
|
186
|
+
index.ts Entry point cron job
|
|
187
|
+
smoke-test.ts Verifica connessione DB
|
|
188
|
+
stats/
|
|
189
|
+
StatisticalEngine.ts Welch's t-test, outlier detection
|
|
190
|
+
TDistribution.ts Distribuzione t (calcolo p-value)
|
|
191
|
+
config/
|
|
192
|
+
AnalysisConfig.ts Soglie configurabili
|
|
193
|
+
env.ts Config OAuth2
|
|
194
|
+
database/
|
|
195
|
+
db.ts Connessione Drizzle + SQLite
|
|
196
|
+
schema.ts Schema database (Drizzle ORM)
|
|
197
|
+
DatabaseService.ts CRUD, transazioni, aggregati, paginazione
|
|
198
|
+
TimeSeriesService.ts Gap detection, recovery
|
|
199
|
+
orchestrator/
|
|
200
|
+
SEOExperimentOrchestrator.ts GSC -> Analisi -> Risultati
|
|
201
|
+
services/
|
|
202
|
+
ExportService.ts Export Excel/CSV
|
|
203
|
+
drizzle/ Migrazioni e meta-dati SQL
|
|
204
|
+
railway.json Config deploy Railway (cron 03:00 UTC)
|
|
205
|
+
Dockerfile Build container Node.js 18+
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
## Limiti dichiarati (by design)
|
|
209
|
+
|
|
210
|
+
- Non funziona su siti piccoli (traffico insufficiente per significativita' statistica)
|
|
211
|
+
- Non e' realtime (richiede tempo per raccogliere dati)
|
|
212
|
+
- Non garantisce risultati (fornisce probabilita', non certezze)
|
|
213
|
+
|
|
214
|
+
Questi limiti aumentano la fiducia nel tool.
|
|
215
|
+
|
|
216
|
+
## Documentazione
|
|
217
|
+
|
|
218
|
+
- **[PROGRESS.md](./PROGRESS.md)** — Progresso implementazione
|
|
219
|
+
- **[blueprint.md](./blueprint.md)** — Specifica tecnica completa
|
|
220
|
+
|
|
221
|
+
## Licenza
|
|
222
|
+
|
|
223
|
+
MIT
|
|
224
|
+
|
|
225
|
+
---
|
|
226
|
+
|
|
227
|
+
**Versione:** 1.0.0 Stable
|
|
228
|
+
**Last Updated:** 2026-02-16
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GoogleOAuthService - Servizio per l'autenticazione OAuth2 con Google
|
|
3
|
+
*/
|
|
4
|
+
interface OAuthConfig {
|
|
5
|
+
clientId: string;
|
|
6
|
+
clientSecret: string;
|
|
7
|
+
redirectUri: string;
|
|
8
|
+
}
|
|
9
|
+
interface TokenResponse {
|
|
10
|
+
access_token: string;
|
|
11
|
+
refresh_token: string;
|
|
12
|
+
expires_in: number;
|
|
13
|
+
token_type: string;
|
|
14
|
+
}
|
|
15
|
+
interface UserInfo {
|
|
16
|
+
email: string;
|
|
17
|
+
verified_email: boolean;
|
|
18
|
+
id: string;
|
|
19
|
+
}
|
|
20
|
+
export declare class GoogleOAuthService {
|
|
21
|
+
private config;
|
|
22
|
+
private sessionStates;
|
|
23
|
+
constructor(config: OAuthConfig);
|
|
24
|
+
getAuthorizationUrl(scopes: string[]): string;
|
|
25
|
+
exchangeCodeForTokens(code: string): Promise<TokenResponse>;
|
|
26
|
+
getUserInfo(accessToken: string): Promise<UserInfo>;
|
|
27
|
+
validateState(state: string): void;
|
|
28
|
+
private generateState;
|
|
29
|
+
}
|
|
30
|
+
export {};
|
|
31
|
+
//# sourceMappingURL=GoogleOAuthService.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"GoogleOAuthService.d.ts","sourceRoot":"","sources":["../../src/auth/GoogleOAuthService.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,UAAU,WAAW;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,UAAU,aAAa;IACrB,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,MAAM,CAAC;IACtB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,UAAU,QAAQ;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,cAAc,EAAE,OAAO,CAAC;IACxB,EAAE,EAAE,MAAM,CAAC;CACZ;AAED,qBAAa,kBAAkB;IAC7B,OAAO,CAAC,MAAM,CAAc;IAC5B,OAAO,CAAC,aAAa,CAA0B;gBAEnC,MAAM,EAAE,WAAW;IAI/B,mBAAmB,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM;IAgBvC,qBAAqB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC;IA2B3D,WAAW,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC;IAezD,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAMlC,OAAO,CAAC,aAAa;CAGtB"}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GoogleOAuthService - Servizio per l'autenticazione OAuth2 con Google
|
|
3
|
+
*/
|
|
4
|
+
export class GoogleOAuthService {
|
|
5
|
+
config;
|
|
6
|
+
sessionStates = new Set();
|
|
7
|
+
constructor(config) {
|
|
8
|
+
this.config = config;
|
|
9
|
+
}
|
|
10
|
+
getAuthorizationUrl(scopes) {
|
|
11
|
+
const state = this.generateState();
|
|
12
|
+
this.sessionStates.add(state);
|
|
13
|
+
const params = new URLSearchParams({
|
|
14
|
+
client_id: this.config.clientId,
|
|
15
|
+
redirect_uri: this.config.redirectUri,
|
|
16
|
+
response_type: 'code',
|
|
17
|
+
scope: scopes.join(' '),
|
|
18
|
+
access_type: 'offline',
|
|
19
|
+
state: state,
|
|
20
|
+
});
|
|
21
|
+
return `https://accounts.google.com/o/oauth2/v2/auth?${params.toString()}`;
|
|
22
|
+
}
|
|
23
|
+
async exchangeCodeForTokens(code) {
|
|
24
|
+
try {
|
|
25
|
+
const response = await fetch('https://oauth2.googleapis.com/token', {
|
|
26
|
+
method: 'POST',
|
|
27
|
+
headers: {
|
|
28
|
+
'Content-Type': 'application/x-www-form-urlencoded',
|
|
29
|
+
},
|
|
30
|
+
body: new URLSearchParams({
|
|
31
|
+
code,
|
|
32
|
+
client_id: this.config.clientId,
|
|
33
|
+
client_secret: this.config.clientSecret,
|
|
34
|
+
redirect_uri: this.config.redirectUri,
|
|
35
|
+
grant_type: 'authorization_code',
|
|
36
|
+
}),
|
|
37
|
+
});
|
|
38
|
+
if (!response.ok) {
|
|
39
|
+
throw new Error('Token exchange failed');
|
|
40
|
+
}
|
|
41
|
+
const data = await response.json();
|
|
42
|
+
return data;
|
|
43
|
+
}
|
|
44
|
+
catch (error) {
|
|
45
|
+
throw new Error('Impossibile connettersi a Google. Riprova.');
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
async getUserInfo(accessToken) {
|
|
49
|
+
const response = await fetch('https://www.googleapis.com/oauth2/v2/userinfo', {
|
|
50
|
+
headers: {
|
|
51
|
+
Authorization: `Bearer ${accessToken}`,
|
|
52
|
+
},
|
|
53
|
+
});
|
|
54
|
+
if (!response.ok) {
|
|
55
|
+
throw new Error('Impossibile connettersi a Google. Riprova.');
|
|
56
|
+
}
|
|
57
|
+
const data = await response.json();
|
|
58
|
+
return data;
|
|
59
|
+
}
|
|
60
|
+
validateState(state) {
|
|
61
|
+
if (!this.sessionStates.has(state)) {
|
|
62
|
+
throw new Error('Impossibile connettersi a Google. Riprova.');
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
generateState() {
|
|
66
|
+
return Math.random().toString(36).substring(2) + Date.now().toString(36);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
//# sourceMappingURL=GoogleOAuthService.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"GoogleOAuthService.js","sourceRoot":"","sources":["../../src/auth/GoogleOAuthService.ts"],"names":[],"mappings":"AAAA;;GAEG;AAqBH,MAAM,OAAO,kBAAkB;IACrB,MAAM,CAAc;IACpB,aAAa,GAAgB,IAAI,GAAG,EAAE,CAAC;IAE/C,YAAY,MAAmB;QAC7B,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;IACvB,CAAC;IAED,mBAAmB,CAAC,MAAgB;QAClC,MAAM,KAAK,GAAG,IAAI,CAAC,aAAa,EAAE,CAAC;QACnC,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QAE9B,MAAM,MAAM,GAAG,IAAI,eAAe,CAAC;YACjC,SAAS,EAAE,IAAI,CAAC,MAAM,CAAC,QAAQ;YAC/B,YAAY,EAAE,IAAI,CAAC,MAAM,CAAC,WAAW;YACrC,aAAa,EAAE,MAAM;YACrB,KAAK,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC;YACvB,WAAW,EAAE,SAAS;YACtB,KAAK,EAAE,KAAK;SACb,CAAC,CAAC;QAEH,OAAO,gDAAgD,MAAM,CAAC,QAAQ,EAAE,EAAE,CAAC;IAC7E,CAAC;IAED,KAAK,CAAC,qBAAqB,CAAC,IAAY;QACtC,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,qCAAqC,EAAE;gBAClE,MAAM,EAAE,MAAM;gBACd,OAAO,EAAE;oBACP,cAAc,EAAE,mCAAmC;iBACpD;gBACD,IAAI,EAAE,IAAI,eAAe,CAAC;oBACxB,IAAI;oBACJ,SAAS,EAAE,IAAI,CAAC,MAAM,CAAC,QAAQ;oBAC/B,aAAa,EAAE,IAAI,CAAC,MAAM,CAAC,YAAY;oBACvC,YAAY,EAAE,IAAI,CAAC,MAAM,CAAC,WAAW;oBACrC,UAAU,EAAE,oBAAoB;iBACjC,CAAC;aACH,CAAC,CAAC;YAEH,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;gBACjB,MAAM,IAAI,KAAK,CAAC,uBAAuB,CAAC,CAAC;YAC3C,CAAC;YAED,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;YACnC,OAAO,IAAqB,CAAC;QAC/B,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,IAAI,KAAK,CAAC,4CAA4C,CAAC,CAAC;QAChE,CAAC;IACH,CAAC;IAED,KAAK,CAAC,WAAW,CAAC,WAAmB;QACnC,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,+CAA+C,EAAE;YAC5E,OAAO,EAAE;gBACP,aAAa,EAAE,UAAU,WAAW,EAAE;aACvC;SACF,CAAC,CAAC;QAEH,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;YACjB,MAAM,IAAI,KAAK,CAAC,4CAA4C,CAAC,CAAC;QAChE,CAAC;QAED,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;QACnC,OAAO,IAAgB,CAAC;IAC1B,CAAC;IAED,aAAa,CAAC,KAAa;QACzB,IAAI,CAAC,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC;YACnC,MAAM,IAAI,KAAK,CAAC,4CAA4C,CAAC,CAAC;QAChE,CAAC;IACH,CAAC;IAEO,aAAa;QACnB,OAAO,IAAI,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;IAC3E,CAAC;CACF"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TokenManager - Gestione dei token OAuth2 e refresh automatico
|
|
3
|
+
*/
|
|
4
|
+
import { GoogleOAuthService } from './GoogleOAuthService.js';
|
|
5
|
+
interface TokenManagerConfig {
|
|
6
|
+
clientId: string;
|
|
7
|
+
clientSecret: string;
|
|
8
|
+
}
|
|
9
|
+
interface StoredToken {
|
|
10
|
+
access_token: string;
|
|
11
|
+
refresh_token: string;
|
|
12
|
+
expires_in: number;
|
|
13
|
+
expires_at: number;
|
|
14
|
+
token_type: string;
|
|
15
|
+
}
|
|
16
|
+
interface TokenResponse {
|
|
17
|
+
access_token: string;
|
|
18
|
+
refresh_token?: string;
|
|
19
|
+
expires_in: number;
|
|
20
|
+
token_type: string;
|
|
21
|
+
}
|
|
22
|
+
interface TokenStatus {
|
|
23
|
+
isRevoked: boolean;
|
|
24
|
+
revokedAt: Date | null;
|
|
25
|
+
}
|
|
26
|
+
interface ValidationResult {
|
|
27
|
+
isValid: boolean;
|
|
28
|
+
isRevoked: boolean;
|
|
29
|
+
errorMessage: string;
|
|
30
|
+
}
|
|
31
|
+
interface ValidTokenResult {
|
|
32
|
+
success: boolean;
|
|
33
|
+
error: string | null;
|
|
34
|
+
token: string | null;
|
|
35
|
+
}
|
|
36
|
+
export declare class TokenManager {
|
|
37
|
+
private config;
|
|
38
|
+
private currentToken;
|
|
39
|
+
private readonly REFRESH_THRESHOLD;
|
|
40
|
+
private tokens;
|
|
41
|
+
private revokedTokens;
|
|
42
|
+
constructor(config: TokenManagerConfig | GoogleOAuthService);
|
|
43
|
+
isTokenExpired(token: StoredToken): boolean;
|
|
44
|
+
processToken(token: TokenResponse): StoredToken;
|
|
45
|
+
needsRefresh(token: StoredToken): boolean;
|
|
46
|
+
refreshAccessToken(userIdOrToken: string): Promise<TokenResponse>;
|
|
47
|
+
saveTokens(userIdOrToken: string | StoredToken, tokens?: TokenResponse): Promise<void>;
|
|
48
|
+
getValidAccessToken(): Promise<string>;
|
|
49
|
+
getValidAccessToken(userId: string): Promise<ValidTokenResult>;
|
|
50
|
+
validateAccessToken(_accessToken: string): Promise<ValidationResult>;
|
|
51
|
+
getTokenStatus(userId: string): Promise<TokenStatus>;
|
|
52
|
+
markAsRevoked(userId: string): Promise<void>;
|
|
53
|
+
isAccessRevoked(userId: string): Promise<boolean>;
|
|
54
|
+
}
|
|
55
|
+
export {};
|
|
56
|
+
//# sourceMappingURL=TokenManager.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TokenManager.d.ts","sourceRoot":"","sources":["../../src/auth/TokenManager.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAE7D,UAAU,kBAAkB;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,UAAU,WAAW;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,MAAM,CAAC;IACtB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,UAAU,aAAa;IACrB,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,UAAU,WAAW;IACnB,SAAS,EAAE,OAAO,CAAC;IACnB,SAAS,EAAE,IAAI,GAAG,IAAI,CAAC;CACxB;AAED,UAAU,gBAAgB;IACxB,OAAO,EAAE,OAAO,CAAC;IACjB,SAAS,EAAE,OAAO,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,UAAU,gBAAgB;IACxB,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;CACtB;AAED,qBAAa,YAAY;IACvB,OAAO,CAAC,MAAM,CAAqB;IACnC,OAAO,CAAC,YAAY,CAA4B;IAChD,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAU;IAC5C,OAAO,CAAC,MAAM,CAAuC;IACrD,OAAO,CAAC,aAAa,CAAgC;gBAEzC,MAAM,EAAE,kBAAkB,GAAG,kBAAkB;IAQ3D,cAAc,CAAC,KAAK,EAAE,WAAW,GAAG,OAAO;IAI3C,YAAY,CAAC,KAAK,EAAE,aAAa,GAAG,WAAW;IAU/C,YAAY,CAAC,KAAK,EAAE,WAAW,GAAG,OAAO;IAKnC,kBAAkB,CAAC,aAAa,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC;IAsFjE,UAAU,CAAC,aAAa,EAAE,MAAM,GAAG,WAAW,EAAE,MAAM,CAAC,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC;IAiBtF,mBAAmB,IAAI,OAAO,CAAC,MAAM,CAAC;IACtC,mBAAmB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAgD9D,mBAAmB,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC;IASpE,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC;IAQpD,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAI5C,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;CAGxD"}
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TokenManager - Gestione dei token OAuth2 e refresh automatico
|
|
3
|
+
*/
|
|
4
|
+
export class TokenManager {
|
|
5
|
+
config;
|
|
6
|
+
currentToken = null;
|
|
7
|
+
REFRESH_THRESHOLD = 600000; // 10 minuti in millisecondi
|
|
8
|
+
tokens = new Map();
|
|
9
|
+
revokedTokens = new Map();
|
|
10
|
+
constructor(config) {
|
|
11
|
+
if ('clientId' in config && 'clientSecret' in config) {
|
|
12
|
+
this.config = config;
|
|
13
|
+
}
|
|
14
|
+
else {
|
|
15
|
+
this.config = { clientId: '', clientSecret: '' };
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
isTokenExpired(token) {
|
|
19
|
+
return token.expires_at < Date.now();
|
|
20
|
+
}
|
|
21
|
+
processToken(token) {
|
|
22
|
+
return {
|
|
23
|
+
access_token: token.access_token,
|
|
24
|
+
refresh_token: token.refresh_token || '',
|
|
25
|
+
expires_in: token.expires_in,
|
|
26
|
+
expires_at: Date.now() + token.expires_in * 1000,
|
|
27
|
+
token_type: token.token_type,
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
needsRefresh(token) {
|
|
31
|
+
const timeUntilExpiry = token.expires_at - Date.now();
|
|
32
|
+
return timeUntilExpiry < this.REFRESH_THRESHOLD;
|
|
33
|
+
}
|
|
34
|
+
async refreshAccessToken(userIdOrToken) {
|
|
35
|
+
// Se è un userId, recupera il token
|
|
36
|
+
let refreshToken;
|
|
37
|
+
let userId = null;
|
|
38
|
+
if (this.tokens.has(userIdOrToken)) {
|
|
39
|
+
userId = userIdOrToken;
|
|
40
|
+
const storedToken = this.tokens.get(userIdOrToken);
|
|
41
|
+
if (!storedToken) {
|
|
42
|
+
throw new Error('No token found');
|
|
43
|
+
}
|
|
44
|
+
// Controlla se è già revocato
|
|
45
|
+
if (this.revokedTokens.has(userId)) {
|
|
46
|
+
throw new Error('Accesso revocato. Riconnetti il tuo account Google.');
|
|
47
|
+
}
|
|
48
|
+
refreshToken = storedToken.refresh_token;
|
|
49
|
+
}
|
|
50
|
+
else {
|
|
51
|
+
refreshToken = userIdOrToken;
|
|
52
|
+
}
|
|
53
|
+
try {
|
|
54
|
+
const response = await fetch('https://oauth2.googleapis.com/token', {
|
|
55
|
+
method: 'POST',
|
|
56
|
+
headers: {
|
|
57
|
+
'Content-Type': 'application/x-www-form-urlencoded',
|
|
58
|
+
},
|
|
59
|
+
body: new URLSearchParams({
|
|
60
|
+
refresh_token: refreshToken,
|
|
61
|
+
client_id: this.config.clientId,
|
|
62
|
+
client_secret: this.config.clientSecret,
|
|
63
|
+
grant_type: 'refresh_token',
|
|
64
|
+
}),
|
|
65
|
+
});
|
|
66
|
+
if (!response.ok) {
|
|
67
|
+
// Prima controlla errori HTTP specifici
|
|
68
|
+
if (response.status === 500) {
|
|
69
|
+
throw new Error('Errore del server Google');
|
|
70
|
+
}
|
|
71
|
+
// Prova a parsare il JSON solo se non è un errore 500
|
|
72
|
+
let data = {};
|
|
73
|
+
try {
|
|
74
|
+
data = await response.json();
|
|
75
|
+
}
|
|
76
|
+
catch {
|
|
77
|
+
// Se non è JSON valido, continua con i controlli di status
|
|
78
|
+
}
|
|
79
|
+
// Controlla se è un errore di revoca
|
|
80
|
+
if (data.error === 'invalid_grant') {
|
|
81
|
+
if (userId) {
|
|
82
|
+
this.revokedTokens.set(userId, new Date());
|
|
83
|
+
}
|
|
84
|
+
throw new Error('Accesso revocato. Riconnetti il tuo account Google.');
|
|
85
|
+
}
|
|
86
|
+
const isRevoked = response.status === 400 || response.status === 401;
|
|
87
|
+
if (isRevoked) {
|
|
88
|
+
throw new Error('Sessione scaduta. Effettua nuovamente il login.');
|
|
89
|
+
}
|
|
90
|
+
throw new Error('Token refresh failed');
|
|
91
|
+
}
|
|
92
|
+
const data = await response.json();
|
|
93
|
+
return data;
|
|
94
|
+
}
|
|
95
|
+
catch (error) {
|
|
96
|
+
if (error instanceof Error) {
|
|
97
|
+
if (error.message.includes('Accesso revocato')) {
|
|
98
|
+
throw error;
|
|
99
|
+
}
|
|
100
|
+
if (error.message.includes('Sessione scaduta')) {
|
|
101
|
+
throw error;
|
|
102
|
+
}
|
|
103
|
+
if (error.message.includes('Errore del server Google')) {
|
|
104
|
+
throw error;
|
|
105
|
+
}
|
|
106
|
+
if (error.message === 'Network timeout') {
|
|
107
|
+
throw new Error('Errore di rete');
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
throw new Error('Impossibile rinnovare la sessione. Riprova.');
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
async saveTokens(userIdOrToken, tokens) {
|
|
114
|
+
if (typeof userIdOrToken === 'string' && tokens) {
|
|
115
|
+
// Nuovo formato: saveTokens(userId, tokens)
|
|
116
|
+
const userId = userIdOrToken;
|
|
117
|
+
const storedToken = this.processToken(tokens);
|
|
118
|
+
this.tokens.set(userId, storedToken);
|
|
119
|
+
// Se salviamo nuovi token, rimuoviamo lo stato di revoca
|
|
120
|
+
if (this.revokedTokens.has(userId)) {
|
|
121
|
+
this.revokedTokens.delete(userId);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
else {
|
|
125
|
+
// Vecchio formato: saveTokens(token)
|
|
126
|
+
this.currentToken = userIdOrToken;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
async getValidAccessToken(userId) {
|
|
130
|
+
if (userId) {
|
|
131
|
+
// Controlla se è revocato
|
|
132
|
+
if (this.revokedTokens.has(userId)) {
|
|
133
|
+
return {
|
|
134
|
+
success: false,
|
|
135
|
+
error: 'Accesso revocato. Riconnetti il tuo account Google.',
|
|
136
|
+
token: null,
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
const token = this.tokens.get(userId);
|
|
140
|
+
if (!token) {
|
|
141
|
+
return {
|
|
142
|
+
success: false,
|
|
143
|
+
error: 'No token available',
|
|
144
|
+
token: null,
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
return {
|
|
148
|
+
success: true,
|
|
149
|
+
error: null,
|
|
150
|
+
token: token.access_token,
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
// Vecchio comportamento
|
|
154
|
+
if (!this.currentToken) {
|
|
155
|
+
throw new Error('No token available');
|
|
156
|
+
}
|
|
157
|
+
if (this.isTokenExpired(this.currentToken) || this.needsRefresh(this.currentToken)) {
|
|
158
|
+
const newTokens = await this.refreshAccessToken(this.currentToken.refresh_token);
|
|
159
|
+
const processedToken = this.processToken(newTokens);
|
|
160
|
+
// Mantieni il refresh token originale se Google non ne invia uno nuovo
|
|
161
|
+
if (!newTokens.refresh_token) {
|
|
162
|
+
processedToken.refresh_token = this.currentToken.refresh_token;
|
|
163
|
+
}
|
|
164
|
+
this.currentToken = processedToken;
|
|
165
|
+
}
|
|
166
|
+
return this.currentToken.access_token;
|
|
167
|
+
}
|
|
168
|
+
async validateAccessToken(_accessToken) {
|
|
169
|
+
// Simula validazione - ritorna revocato per token non autorizzati
|
|
170
|
+
return {
|
|
171
|
+
isValid: false,
|
|
172
|
+
isRevoked: true,
|
|
173
|
+
errorMessage: 'Accesso revocato. Riconnetti il tuo account Google.',
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
async getTokenStatus(userId) {
|
|
177
|
+
const isRevoked = this.revokedTokens.has(userId);
|
|
178
|
+
return {
|
|
179
|
+
isRevoked,
|
|
180
|
+
revokedAt: isRevoked ? this.revokedTokens.get(userId) : null,
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
async markAsRevoked(userId) {
|
|
184
|
+
this.revokedTokens.set(userId, new Date());
|
|
185
|
+
}
|
|
186
|
+
async isAccessRevoked(userId) {
|
|
187
|
+
return this.revokedTokens.has(userId);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
//# sourceMappingURL=TokenManager.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TokenManager.js","sourceRoot":"","sources":["../../src/auth/TokenManager.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CLI Commands
|
|
3
|
+
*
|
|
4
|
+
* Implementazione dei 7 comandi: login, add, list, status, run, export, delete
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* login — Collega l'account Google per accedere a Google Search Console
|
|
8
|
+
*/
|
|
9
|
+
export declare function loginCommand(): Promise<void>;
|
|
10
|
+
/**
|
|
11
|
+
* add — Crea un nuovo test SEO con prompt interattivi
|
|
12
|
+
*/
|
|
13
|
+
export declare function addCommand(): Promise<void>;
|
|
14
|
+
/**
|
|
15
|
+
* list — Mostra tabella con tutti i test
|
|
16
|
+
*/
|
|
17
|
+
export declare function listCommand(): Promise<void>;
|
|
18
|
+
/**
|
|
19
|
+
* status <id> — Dettaglio di un singolo test con analisi e grafico
|
|
20
|
+
*/
|
|
21
|
+
export declare function statusCommand(id: string): Promise<void>;
|
|
22
|
+
/**
|
|
23
|
+
* run — Sincronizza tutti i test attivi via orchestratore
|
|
24
|
+
*/
|
|
25
|
+
export declare function runCommand(): Promise<void>;
|
|
26
|
+
/**
|
|
27
|
+
* export <id> — Esporta dati test su file Excel o CSV (con scelta interattiva)
|
|
28
|
+
*/
|
|
29
|
+
export declare function exportCommand(id: string, options?: {
|
|
30
|
+
format?: string;
|
|
31
|
+
}): Promise<void>;
|
|
32
|
+
/**
|
|
33
|
+
* delete <id> — Elimina un test (con conferma interattiva) e registra nell'Audit Log
|
|
34
|
+
*/
|
|
35
|
+
export declare function deleteCommand(id: string): Promise<void>;
|
|
36
|
+
//# sourceMappingURL=commands.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"commands.d.ts","sourceRoot":"","sources":["../../src/cli/commands.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAsEH;;GAEG;AACH,wBAAsB,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC,CA4FlD;AAED;;GAEG;AACH,wBAAsB,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC,CA8DhD;AAED;;GAEG;AACH,wBAAsB,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC,CAmBjD;AAED;;GAEG;AACH,wBAAsB,aAAa,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CA2D7D;AAED;;GAEG;AACH,wBAAsB,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC,CAoEhD;AAED;;GAEG;AACH,wBAAsB,aAAa,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;IAAE,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CA6E5F;AAED;;GAEG;AACH,wBAAsB,aAAa,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAmD7D"}
|