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
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CLI Formatters
|
|
3
|
+
*
|
|
4
|
+
* Colori, tabelle e grafici ASCII per il terminale.
|
|
5
|
+
*/
|
|
6
|
+
import chalk from 'chalk';
|
|
7
|
+
import Table from 'cli-table3';
|
|
8
|
+
// @ts-ignore - no type declarations available
|
|
9
|
+
import asciichart from 'asciichart';
|
|
10
|
+
// --- Colori ---
|
|
11
|
+
export const colors = {
|
|
12
|
+
significant: chalk.green.bold,
|
|
13
|
+
uncertain: chalk.yellow,
|
|
14
|
+
notSignificant: chalk.gray,
|
|
15
|
+
error: chalk.red.bold,
|
|
16
|
+
success: chalk.green,
|
|
17
|
+
info: chalk.cyan,
|
|
18
|
+
header: chalk.white.bold,
|
|
19
|
+
muted: chalk.gray,
|
|
20
|
+
value: chalk.white,
|
|
21
|
+
};
|
|
22
|
+
export function colorPValue(p) {
|
|
23
|
+
if (p === null)
|
|
24
|
+
return colors.muted('—');
|
|
25
|
+
if (p < 0.05)
|
|
26
|
+
return colors.significant(p.toFixed(4));
|
|
27
|
+
if (p < 0.10)
|
|
28
|
+
return colors.uncertain(p.toFixed(4));
|
|
29
|
+
return colors.notSignificant(p.toFixed(4));
|
|
30
|
+
}
|
|
31
|
+
export function colorStatus(status) {
|
|
32
|
+
switch (status) {
|
|
33
|
+
case 'completed': return colors.significant(status);
|
|
34
|
+
case 'running': return colors.uncertain(status);
|
|
35
|
+
case 'failed': return colors.error(status);
|
|
36
|
+
case 'paused': return colors.muted(status);
|
|
37
|
+
default: return status;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
export function colorImprovement(pct) {
|
|
41
|
+
if (pct === null)
|
|
42
|
+
return colors.muted('—');
|
|
43
|
+
const sign = pct >= 0 ? '+' : '';
|
|
44
|
+
const formatted = `${sign}${pct.toFixed(1)}%`;
|
|
45
|
+
if (pct > 0)
|
|
46
|
+
return chalk.green(formatted);
|
|
47
|
+
if (pct < 0)
|
|
48
|
+
return chalk.red(formatted);
|
|
49
|
+
return colors.muted(formatted);
|
|
50
|
+
}
|
|
51
|
+
// --- Tabelle ---
|
|
52
|
+
export function formatTestTable(tests) {
|
|
53
|
+
const table = new Table({
|
|
54
|
+
head: [
|
|
55
|
+
chalk.cyan('ID'),
|
|
56
|
+
chalk.cyan('Nome'),
|
|
57
|
+
chalk.cyan('Stato'),
|
|
58
|
+
chalk.cyan('p-Value'),
|
|
59
|
+
chalk.cyan('Migliora.'),
|
|
60
|
+
chalk.cyan('Ultimo Sync'),
|
|
61
|
+
],
|
|
62
|
+
style: { head: [], border: [] },
|
|
63
|
+
});
|
|
64
|
+
for (const t of tests) {
|
|
65
|
+
const shortId = t.id.substring(0, 8);
|
|
66
|
+
const syncDate = t.lastSyncAt
|
|
67
|
+
? new Date(t.lastSyncAt).toLocaleDateString('it-IT')
|
|
68
|
+
: colors.muted('mai');
|
|
69
|
+
table.push([
|
|
70
|
+
colors.muted(shortId),
|
|
71
|
+
t.name,
|
|
72
|
+
colorStatus(t.status),
|
|
73
|
+
colorPValue(t.lastPValue),
|
|
74
|
+
colorImprovement(t.lastImprovement),
|
|
75
|
+
syncDate,
|
|
76
|
+
]);
|
|
77
|
+
}
|
|
78
|
+
return table.toString();
|
|
79
|
+
}
|
|
80
|
+
export function formatStatusDetail(test, analysis) {
|
|
81
|
+
const lines = [];
|
|
82
|
+
const divider = colors.muted('─'.repeat(50));
|
|
83
|
+
lines.push('');
|
|
84
|
+
lines.push(colors.header(` ${test.name}`));
|
|
85
|
+
lines.push(divider);
|
|
86
|
+
lines.push(` ${colors.muted('ID:')} ${test.id}`);
|
|
87
|
+
lines.push(` ${colors.muted('Sito:')} ${test.siteUrl}`);
|
|
88
|
+
lines.push(` ${colors.muted('Stato:')} ${colorStatus(test.status)}`);
|
|
89
|
+
lines.push(` ${colors.muted('Inizio:')} ${new Date(test.startDate).toLocaleDateString('it-IT')}`);
|
|
90
|
+
lines.push(` ${colors.muted('Split Date:')} ${new Date(test.splitDate).toLocaleDateString('it-IT')}`);
|
|
91
|
+
lines.push('');
|
|
92
|
+
lines.push(colors.header(' Analisi Statistica'));
|
|
93
|
+
lines.push(divider);
|
|
94
|
+
const sigLabel = analysis.isSignificant
|
|
95
|
+
? colors.significant('SIGNIFICATIVO')
|
|
96
|
+
: analysis.hasInsufficientData
|
|
97
|
+
? colors.uncertain('DATI INSUFFICIENTI')
|
|
98
|
+
: colors.muted('Non significativo');
|
|
99
|
+
lines.push(` ${colors.muted('Risultato:')} ${sigLabel}`);
|
|
100
|
+
lines.push(` ${colors.muted('p-Value:')} ${colorPValue(analysis.pValue)}`);
|
|
101
|
+
const arrow = analysis.percentageChange >= 0 ? chalk.green('↑') : chalk.red('↓');
|
|
102
|
+
lines.push(` ${colors.muted('Variazione:')} ${arrow} ${colorImprovement(analysis.percentageChange)}`);
|
|
103
|
+
if (analysis.tStatistic !== undefined) {
|
|
104
|
+
lines.push(` ${colors.muted('t-Statistic:')} ${colors.value(analysis.tStatistic.toFixed(4))}`);
|
|
105
|
+
}
|
|
106
|
+
if (analysis.degreesOfFreedom !== undefined) {
|
|
107
|
+
lines.push(` ${colors.muted('Gradi lib.:')} ${colors.value(analysis.degreesOfFreedom.toFixed(1))}`);
|
|
108
|
+
}
|
|
109
|
+
lines.push('');
|
|
110
|
+
lines.push(colors.header(' Dati'));
|
|
111
|
+
lines.push(divider);
|
|
112
|
+
// Calcola medie
|
|
113
|
+
const beforeClicks = test._beforeClicks;
|
|
114
|
+
const afterClicks = test._afterClicks;
|
|
115
|
+
const meanBefore = beforeClicks && beforeClicks.length > 0
|
|
116
|
+
? (beforeClicks.reduce((a, b) => a + b, 0) / beforeClicks.length).toFixed(1)
|
|
117
|
+
: '—';
|
|
118
|
+
const meanAfter = afterClicks && afterClicks.length > 0
|
|
119
|
+
? (afterClicks.reduce((a, b) => a + b, 0) / afterClicks.length).toFixed(1)
|
|
120
|
+
: '—';
|
|
121
|
+
lines.push(` ${colors.muted('Before:')} ${colors.value(meanBefore)} media clicks (${beforeClicks?.length ?? 0} giorni)`);
|
|
122
|
+
lines.push(` ${colors.muted('After:')} ${colors.value(meanAfter)} media clicks (${afterClicks?.length ?? 0} giorni)`);
|
|
123
|
+
if (analysis.outliersDetected) {
|
|
124
|
+
lines.push(` ${colors.uncertain(`Outlier rilevati: ${analysis.outliers.length}`)}`);
|
|
125
|
+
}
|
|
126
|
+
if (analysis.seasonalityDetected) {
|
|
127
|
+
lines.push(` ${colors.info('Stagionalità rilevata (periodo 7 giorni)')}`);
|
|
128
|
+
}
|
|
129
|
+
lines.push('');
|
|
130
|
+
return lines.join('\n');
|
|
131
|
+
}
|
|
132
|
+
// --- Grafici ---
|
|
133
|
+
export function renderClicksChart(metrics, splitDate) {
|
|
134
|
+
if (metrics.length === 0)
|
|
135
|
+
return colors.muted(' Nessun dato disponibile per il grafico.');
|
|
136
|
+
const sorted = [...metrics].sort((a, b) => new Date(a.date).getTime() - new Date(b.date).getTime());
|
|
137
|
+
const clicks = sorted.map((m) => m.clicks);
|
|
138
|
+
// Trova indice splitDate
|
|
139
|
+
const splitTime = splitDate.getTime();
|
|
140
|
+
let splitIndex = sorted.findIndex((m) => new Date(m.date).getTime() >= splitTime);
|
|
141
|
+
if (splitIndex === -1)
|
|
142
|
+
splitIndex = sorted.length;
|
|
143
|
+
const chartHeight = Math.min(12, Math.max(4, clicks.length > 0 ? 8 : 4));
|
|
144
|
+
const chart = asciichart.plot(clicks, {
|
|
145
|
+
height: chartHeight,
|
|
146
|
+
format: (x) => String(Math.round(x)).padStart(6),
|
|
147
|
+
});
|
|
148
|
+
const lines = [];
|
|
149
|
+
lines.push('');
|
|
150
|
+
lines.push(colors.header(' Trend Clicks'));
|
|
151
|
+
lines.push(colors.muted(' ' + '─'.repeat(50)));
|
|
152
|
+
lines.push('');
|
|
153
|
+
// Aggiungi il grafico con padding
|
|
154
|
+
for (const line of chart.split('\n')) {
|
|
155
|
+
lines.push(' ' + line);
|
|
156
|
+
}
|
|
157
|
+
// Indicatore splitDate sotto il grafico
|
|
158
|
+
// L'offset del grafico è ~8 caratteri (asse Y labels)
|
|
159
|
+
const chartOffset = 8;
|
|
160
|
+
if (splitIndex > 0 && splitIndex < sorted.length) {
|
|
161
|
+
const markerPos = chartOffset + splitIndex;
|
|
162
|
+
const markerLine = ' '.repeat(markerPos) + chalk.yellow('▲');
|
|
163
|
+
const labelLine = ' '.repeat(Math.max(0, markerPos - 5)) + chalk.yellow('Split Date');
|
|
164
|
+
lines.push(' ' + markerLine);
|
|
165
|
+
lines.push(' ' + labelLine);
|
|
166
|
+
}
|
|
167
|
+
// Asse X: prima e ultima data
|
|
168
|
+
const firstDate = new Date(sorted[0].date).toLocaleDateString('it-IT');
|
|
169
|
+
const lastDate = new Date(sorted[sorted.length - 1].date).toLocaleDateString('it-IT');
|
|
170
|
+
lines.push('');
|
|
171
|
+
lines.push(` ${colors.muted(firstDate)}${' '.repeat(Math.max(2, 40 - firstDate.length - lastDate.length))}${colors.muted(lastDate)}`);
|
|
172
|
+
lines.push('');
|
|
173
|
+
return lines.join('\n');
|
|
174
|
+
}
|
|
175
|
+
//# sourceMappingURL=formatters.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"formatters.js","sourceRoot":"","sources":["../../src/cli/formatters.ts"],"names":[],"mappings":"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"}
|
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* SEO Testing Tool — CLI
|
|
4
|
+
*
|
|
5
|
+
* Interfaccia a riga di comando per gestire esperimenti SEO:
|
|
6
|
+
* seo-tool login Collega account Google
|
|
7
|
+
* seo-tool add Crea un nuovo test
|
|
8
|
+
* seo-tool list Mostra tutti i test
|
|
9
|
+
* seo-tool status <id> Dettaglio test con grafico
|
|
10
|
+
* seo-tool run Sincronizza test attivi
|
|
11
|
+
* seo-tool export <id> Esporta dati in CSV
|
|
12
|
+
* seo-tool delete <id> Elimina un test
|
|
13
|
+
*/
|
|
14
|
+
export {};
|
|
15
|
+
//# sourceMappingURL=cli.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AAEA;;;;;;;;;;;GAWG"}
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* SEO Testing Tool — CLI
|
|
4
|
+
*
|
|
5
|
+
* Interfaccia a riga di comando per gestire esperimenti SEO:
|
|
6
|
+
* seo-tool login Collega account Google
|
|
7
|
+
* seo-tool add Crea un nuovo test
|
|
8
|
+
* seo-tool list Mostra tutti i test
|
|
9
|
+
* seo-tool status <id> Dettaglio test con grafico
|
|
10
|
+
* seo-tool run Sincronizza test attivi
|
|
11
|
+
* seo-tool export <id> Esporta dati in CSV
|
|
12
|
+
* seo-tool delete <id> Elimina un test
|
|
13
|
+
*/
|
|
14
|
+
import { config } from 'dotenv';
|
|
15
|
+
config();
|
|
16
|
+
import { createRequire } from 'module';
|
|
17
|
+
import { Command } from 'commander';
|
|
18
|
+
import { migrateDB } from './database/db.js';
|
|
19
|
+
const require = createRequire(import.meta.url);
|
|
20
|
+
const { version } = require('../package.json');
|
|
21
|
+
import { loginCommand, addCommand, listCommand, statusCommand, runCommand, exportCommand, deleteCommand, } from './cli/commands.js';
|
|
22
|
+
// Auto-setup: crea DB e tabelle al primo avvio
|
|
23
|
+
migrateDB();
|
|
24
|
+
const program = new Command();
|
|
25
|
+
program
|
|
26
|
+
.name('seo-tool')
|
|
27
|
+
.description('SEO Testing Tool — Analisi statistica per esperimenti SEO')
|
|
28
|
+
.version(version);
|
|
29
|
+
program
|
|
30
|
+
.command('login')
|
|
31
|
+
.description('Collega il tuo account Google per accedere a Search Console')
|
|
32
|
+
.action(loginCommand);
|
|
33
|
+
program
|
|
34
|
+
.command('add')
|
|
35
|
+
.description('Crea un nuovo test SEO')
|
|
36
|
+
.action(addCommand);
|
|
37
|
+
program
|
|
38
|
+
.command('list')
|
|
39
|
+
.description('Mostra tutti i test')
|
|
40
|
+
.action(listCommand);
|
|
41
|
+
program
|
|
42
|
+
.command('status')
|
|
43
|
+
.argument('<id>', 'ID del test (anche parziale)')
|
|
44
|
+
.description('Mostra dettaglio test con analisi e grafico')
|
|
45
|
+
.action(statusCommand);
|
|
46
|
+
program
|
|
47
|
+
.command('run')
|
|
48
|
+
.description('Sincronizza tutti i test attivi (per cron job)')
|
|
49
|
+
.action(runCommand);
|
|
50
|
+
program
|
|
51
|
+
.command('export')
|
|
52
|
+
.argument('<id>', 'ID del test (anche parziale)')
|
|
53
|
+
.option('--format <fmt>', 'Formato export: xlsx o csv (interattivo se omesso)')
|
|
54
|
+
.description('Esporta dati test su file Excel o CSV')
|
|
55
|
+
.action(exportCommand);
|
|
56
|
+
program
|
|
57
|
+
.command('delete')
|
|
58
|
+
.argument('<id>', 'ID del test (anche parziale)')
|
|
59
|
+
.description('Elimina un test e le metriche collegate')
|
|
60
|
+
.action(deleteCommand);
|
|
61
|
+
program.parse();
|
|
62
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AAEA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAChC,MAAM,EAAE,CAAC;AAET,OAAO,EAAE,aAAa,EAAE,MAAM,QAAQ,CAAC;AACvC,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAE7C,MAAM,OAAO,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAC/C,MAAM,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,iBAAiB,CAAwB,CAAC;AACtE,OAAO,EACL,YAAY,EACZ,UAAU,EACV,WAAW,EACX,aAAa,EACb,UAAU,EACV,aAAa,EACb,aAAa,GACd,MAAM,mBAAmB,CAAC;AAE3B,+CAA+C;AAC/C,SAAS,EAAE,CAAC;AAEZ,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAC;AAE9B,OAAO;KACJ,IAAI,CAAC,UAAU,CAAC;KAChB,WAAW,CAAC,2DAA2D,CAAC;KACxE,OAAO,CAAC,OAAO,CAAC,CAAC;AAEpB,OAAO;KACJ,OAAO,CAAC,OAAO,CAAC;KAChB,WAAW,CAAC,6DAA6D,CAAC;KAC1E,MAAM,CAAC,YAAY,CAAC,CAAC;AAExB,OAAO;KACJ,OAAO,CAAC,KAAK,CAAC;KACd,WAAW,CAAC,wBAAwB,CAAC;KACrC,MAAM,CAAC,UAAU,CAAC,CAAC;AAEtB,OAAO;KACJ,OAAO,CAAC,MAAM,CAAC;KACf,WAAW,CAAC,qBAAqB,CAAC;KAClC,MAAM,CAAC,WAAW,CAAC,CAAC;AAEvB,OAAO;KACJ,OAAO,CAAC,QAAQ,CAAC;KACjB,QAAQ,CAAC,MAAM,EAAE,8BAA8B,CAAC;KAChD,WAAW,CAAC,6CAA6C,CAAC;KAC1D,MAAM,CAAC,aAAa,CAAC,CAAC;AAEzB,OAAO;KACJ,OAAO,CAAC,KAAK,CAAC;KACd,WAAW,CAAC,gDAAgD,CAAC;KAC7D,MAAM,CAAC,UAAU,CAAC,CAAC;AAEtB,OAAO;KACJ,OAAO,CAAC,QAAQ,CAAC;KACjB,QAAQ,CAAC,MAAM,EAAE,8BAA8B,CAAC;KAChD,MAAM,CAAC,gBAAgB,EAAE,oDAAoD,CAAC;KAC9E,WAAW,CAAC,uCAAuC,CAAC;KACpD,MAAM,CAAC,aAAa,CAAC,CAAC;AAEzB,OAAO;KACJ,OAAO,CAAC,QAAQ,CAAC;KACjB,QAAQ,CAAC,MAAM,EAAE,8BAA8B,CAAC;KAChD,WAAW,CAAC,yCAAyC,CAAC;KACtD,MAAM,CAAC,aAAa,CAAC,CAAC;AAEzB,OAAO,CAAC,KAAK,EAAE,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export interface AnalysisConfig {
|
|
2
|
+
/** Soglia p-value sotto la quale il risultato è significativo (default: 0.05) */
|
|
3
|
+
significanceLevel: number;
|
|
4
|
+
/** Minimo click/giorno medio richiesto in entrambi i periodi (default: 50) */
|
|
5
|
+
minimumDataThreshold: number;
|
|
6
|
+
/** Moltiplicatore IQR per rilevamento outlier. 3.0 = conservativo, 1.5 = aggressivo (default: 3.0) */
|
|
7
|
+
outlierIqrMultiplier: number;
|
|
8
|
+
/** Minimo numero di data point per periodo richiesto per il t-test (default: 7) */
|
|
9
|
+
minimumSampleSize: number;
|
|
10
|
+
}
|
|
11
|
+
export declare const DEFAULT_ANALYSIS_CONFIG: Readonly<AnalysisConfig>;
|
|
12
|
+
export declare function createAnalysisConfig(overrides?: Partial<AnalysisConfig>): AnalysisConfig;
|
|
13
|
+
//# sourceMappingURL=AnalysisConfig.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AnalysisConfig.d.ts","sourceRoot":"","sources":["../../src/config/AnalysisConfig.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,cAAc;IAC7B,iFAAiF;IACjF,iBAAiB,EAAE,MAAM,CAAC;IAC1B,8EAA8E;IAC9E,oBAAoB,EAAE,MAAM,CAAC;IAC7B,sGAAsG;IACtG,oBAAoB,EAAE,MAAM,CAAC;IAC7B,mFAAmF;IACnF,iBAAiB,EAAE,MAAM,CAAC;CAC3B;AAED,eAAO,MAAM,uBAAuB,EAAE,QAAQ,CAAC,cAAc,CAK5D,CAAC;AAEF,wBAAgB,oBAAoB,CAAC,SAAS,CAAC,EAAE,OAAO,CAAC,cAAc,CAAC,GAAG,cAAc,CAExF"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export const DEFAULT_ANALYSIS_CONFIG = {
|
|
2
|
+
significanceLevel: 0.05,
|
|
3
|
+
minimumDataThreshold: 50,
|
|
4
|
+
outlierIqrMultiplier: 3.0,
|
|
5
|
+
minimumSampleSize: 7,
|
|
6
|
+
};
|
|
7
|
+
export function createAnalysisConfig(overrides) {
|
|
8
|
+
return { ...DEFAULT_ANALYSIS_CONFIG, ...overrides };
|
|
9
|
+
}
|
|
10
|
+
//# sourceMappingURL=AnalysisConfig.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AnalysisConfig.js","sourceRoot":"","sources":["../../src/config/AnalysisConfig.ts"],"names":[],"mappings":"AAWA,MAAM,CAAC,MAAM,uBAAuB,GAA6B;IAC/D,iBAAiB,EAAE,IAAI;IACvB,oBAAoB,EAAE,EAAE;IACxB,oBAAoB,EAAE,GAAG;IACzB,iBAAiB,EAAE,CAAC;CACrB,CAAC;AAEF,MAAM,UAAU,oBAAoB,CAAC,SAAmC;IACtE,OAAO,EAAE,GAAG,uBAAuB,EAAE,GAAG,SAAS,EAAE,CAAC;AACtD,CAAC"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Environment Configuration
|
|
3
|
+
* Carica le credenziali Google OAuth2 in modo sicuro da .env
|
|
4
|
+
*/
|
|
5
|
+
export interface GoogleOAuthConfig {
|
|
6
|
+
clientId: string;
|
|
7
|
+
clientSecret: string;
|
|
8
|
+
redirectUri: string;
|
|
9
|
+
authUri: string;
|
|
10
|
+
tokenUri: string;
|
|
11
|
+
scopes: string[];
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Carica configurazione Google OAuth2 dalle variabili d'ambiente
|
|
15
|
+
*
|
|
16
|
+
* @throws Error se le credenziali non sono configurate
|
|
17
|
+
*/
|
|
18
|
+
export declare function getGoogleOAuthConfig(): GoogleOAuthConfig;
|
|
19
|
+
/**
|
|
20
|
+
* Carica configurazione da file client_secret_*.json (fallback)
|
|
21
|
+
* ATTENZIONE: Usare solo per test locali, non in produzione!
|
|
22
|
+
*
|
|
23
|
+
* @param filePath Percorso al file client_secret JSON
|
|
24
|
+
*/
|
|
25
|
+
export declare function loadCredentialsFromFile(filePath: string): GoogleOAuthConfig;
|
|
26
|
+
/**
|
|
27
|
+
* Helper per test: carica credenziali da .env o file
|
|
28
|
+
*/
|
|
29
|
+
export declare function getTestCredentials(): GoogleOAuthConfig;
|
|
30
|
+
//# sourceMappingURL=env.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"env.d.ts","sourceRoot":"","sources":["../../src/config/env.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAKH,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,IAAI,iBAAiB,CAyBxD;AAED;;;;;GAKG;AACH,wBAAgB,uBAAuB,CAAC,QAAQ,EAAE,MAAM,GAAG,iBAAiB,CAsB3E;AAED;;GAEG;AACH,wBAAgB,kBAAkB,IAAI,iBAAiB,CActD"}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Environment Configuration
|
|
3
|
+
* Carica le credenziali Google OAuth2 in modo sicuro da .env
|
|
4
|
+
*/
|
|
5
|
+
import { readFileSync, readdirSync } from 'fs';
|
|
6
|
+
import path from 'path';
|
|
7
|
+
/**
|
|
8
|
+
* Carica configurazione Google OAuth2 dalle variabili d'ambiente
|
|
9
|
+
*
|
|
10
|
+
* @throws Error se le credenziali non sono configurate
|
|
11
|
+
*/
|
|
12
|
+
export function getGoogleOAuthConfig() {
|
|
13
|
+
const clientId = process.env.GOOGLE_CLIENT_ID;
|
|
14
|
+
const clientSecret = process.env.GOOGLE_CLIENT_SECRET;
|
|
15
|
+
const redirectUri = process.env.GOOGLE_REDIRECT_URI || 'http://localhost';
|
|
16
|
+
const authUri = process.env.GOOGLE_AUTH_URI || 'https://accounts.google.com/o/oauth2/auth';
|
|
17
|
+
const tokenUri = process.env.GOOGLE_TOKEN_URI || 'https://oauth2.googleapis.com/token';
|
|
18
|
+
const scopesStr = process.env.GOOGLE_SCOPES ||
|
|
19
|
+
'https://www.googleapis.com/auth/webmasters.readonly,https://www.googleapis.com/auth/userinfo.email';
|
|
20
|
+
if (!clientId || !clientSecret) {
|
|
21
|
+
throw new Error('Google OAuth credentials not configured. ' +
|
|
22
|
+
'Please create a .env file with GOOGLE_CLIENT_ID and GOOGLE_CLIENT_SECRET. ' +
|
|
23
|
+
'See .env.example for reference.');
|
|
24
|
+
}
|
|
25
|
+
return {
|
|
26
|
+
clientId,
|
|
27
|
+
clientSecret,
|
|
28
|
+
redirectUri,
|
|
29
|
+
authUri,
|
|
30
|
+
tokenUri,
|
|
31
|
+
scopes: scopesStr.split(',').map(s => s.trim()),
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Carica configurazione da file client_secret_*.json (fallback)
|
|
36
|
+
* ATTENZIONE: Usare solo per test locali, non in produzione!
|
|
37
|
+
*
|
|
38
|
+
* @param filePath Percorso al file client_secret JSON
|
|
39
|
+
*/
|
|
40
|
+
export function loadCredentialsFromFile(filePath) {
|
|
41
|
+
const content = readFileSync(filePath, 'utf-8');
|
|
42
|
+
const credentials = JSON.parse(content);
|
|
43
|
+
// Il file può avere formato "installed" o "web"
|
|
44
|
+
const creds = credentials.installed || credentials.web;
|
|
45
|
+
if (!creds) {
|
|
46
|
+
throw new Error('Invalid client_secret file format');
|
|
47
|
+
}
|
|
48
|
+
return {
|
|
49
|
+
clientId: creds.client_id,
|
|
50
|
+
clientSecret: creds.client_secret,
|
|
51
|
+
redirectUri: creds.redirect_uris[0],
|
|
52
|
+
authUri: creds.auth_uri,
|
|
53
|
+
tokenUri: creds.token_uri,
|
|
54
|
+
scopes: [
|
|
55
|
+
'https://www.googleapis.com/auth/webmasters.readonly',
|
|
56
|
+
'https://www.googleapis.com/auth/userinfo.email',
|
|
57
|
+
],
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Helper per test: carica credenziali da .env o file
|
|
62
|
+
*/
|
|
63
|
+
export function getTestCredentials() {
|
|
64
|
+
try {
|
|
65
|
+
return getGoogleOAuthConfig();
|
|
66
|
+
}
|
|
67
|
+
catch {
|
|
68
|
+
// Fallback: prova a caricare dal file client_secret
|
|
69
|
+
const files = readdirSync(process.cwd());
|
|
70
|
+
const secretFile = files.find((f) => f.startsWith('client_secret_') && f.endsWith('.json'));
|
|
71
|
+
if (secretFile) {
|
|
72
|
+
return loadCredentialsFromFile(path.join(process.cwd(), secretFile));
|
|
73
|
+
}
|
|
74
|
+
throw new Error('No credentials found. Configure .env or add client_secret file.');
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
//# sourceMappingURL=env.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"env.js","sourceRoot":"","sources":["../../src/config/env.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,IAAI,CAAC;AAC/C,OAAO,IAAI,MAAM,MAAM,CAAC;AAWxB;;;;GAIG;AACH,MAAM,UAAU,oBAAoB;IAClC,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC;IAC9C,MAAM,YAAY,GAAG,OAAO,CAAC,GAAG,CAAC,oBAAoB,CAAC;IACtD,MAAM,WAAW,GAAG,OAAO,CAAC,GAAG,CAAC,mBAAmB,IAAI,kBAAkB,CAAC;IAC1E,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,eAAe,IAAI,2CAA2C,CAAC;IAC3F,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,gBAAgB,IAAI,qCAAqC,CAAC;IACvF,MAAM,SAAS,GAAG,OAAO,CAAC,GAAG,CAAC,aAAa;QACzC,oGAAoG,CAAC;IAEvG,IAAI,CAAC,QAAQ,IAAI,CAAC,YAAY,EAAE,CAAC;QAC/B,MAAM,IAAI,KAAK,CACb,2CAA2C;YAC3C,4EAA4E;YAC5E,iCAAiC,CAClC,CAAC;IACJ,CAAC;IAED,OAAO;QACL,QAAQ;QACR,YAAY;QACZ,WAAW;QACX,OAAO;QACP,QAAQ;QACR,MAAM,EAAE,SAAS,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;KAChD,CAAC;AACJ,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,uBAAuB,CAAC,QAAgB;IACtD,MAAM,OAAO,GAAG,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;IAChD,MAAM,WAAW,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IAExC,gDAAgD;IAChD,MAAM,KAAK,GAAG,WAAW,CAAC,SAAS,IAAI,WAAW,CAAC,GAAG,CAAC;IAEvD,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,MAAM,IAAI,KAAK,CAAC,mCAAmC,CAAC,CAAC;IACvD,CAAC;IAED,OAAO;QACL,QAAQ,EAAE,KAAK,CAAC,SAAS;QACzB,YAAY,EAAE,KAAK,CAAC,aAAa;QACjC,WAAW,EAAE,KAAK,CAAC,aAAa,CAAC,CAAC,CAAC;QACnC,OAAO,EAAE,KAAK,CAAC,QAAQ;QACvB,QAAQ,EAAE,KAAK,CAAC,SAAS;QACzB,MAAM,EAAE;YACN,qDAAqD;YACrD,gDAAgD;SACjD;KACF,CAAC;AACJ,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,kBAAkB;IAChC,IAAI,CAAC;QACH,OAAO,oBAAoB,EAAE,CAAC;IAChC,CAAC;IAAC,MAAM,CAAC;QACP,oDAAoD;QACpD,MAAM,KAAK,GAAG,WAAW,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC;QACzC,MAAM,UAAU,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,CAAS,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,gBAAgB,CAAC,IAAI,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC;QAEpG,IAAI,UAAU,EAAE,CAAC;YACf,OAAO,uBAAuB,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,UAAU,CAAC,CAAC,CAAC;QACvE,CAAC;QAED,MAAM,IAAI,KAAK,CAAC,iEAAiE,CAAC,CAAC;IACrF,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DatabaseService
|
|
3
|
+
*
|
|
4
|
+
* Gestisce operazioni sul database con Drizzle ORM:
|
|
5
|
+
* - Concorrenza e transazioni atomiche
|
|
6
|
+
* - Performance su grandi dataset con query aggregate
|
|
7
|
+
* - Paginazione e retention policy
|
|
8
|
+
*/
|
|
9
|
+
import { type DrizzleDB } from './db.js';
|
|
10
|
+
export declare class DatabaseService {
|
|
11
|
+
private db;
|
|
12
|
+
constructor(drizzleDb?: DrizzleDB);
|
|
13
|
+
/**
|
|
14
|
+
* Test 5.1 - Recupera report test per utente
|
|
15
|
+
*/
|
|
16
|
+
getTestReport(testId: string, userId: string): Promise<{
|
|
17
|
+
testId: string;
|
|
18
|
+
userId: string;
|
|
19
|
+
name: string;
|
|
20
|
+
data: {
|
|
21
|
+
id: number;
|
|
22
|
+
testId: string;
|
|
23
|
+
date: string;
|
|
24
|
+
clicks: number;
|
|
25
|
+
impressions: number;
|
|
26
|
+
gapFilled: boolean;
|
|
27
|
+
filledAt: string | null;
|
|
28
|
+
createdAt: string;
|
|
29
|
+
}[];
|
|
30
|
+
} | null>;
|
|
31
|
+
/**
|
|
32
|
+
* Test 5.1 - Aggiorna test (con gestione race condition)
|
|
33
|
+
*/
|
|
34
|
+
updateTest(testId: string, updates: Record<string, unknown>, userId: string): Promise<{
|
|
35
|
+
updatedAt: number;
|
|
36
|
+
matchedCount: number;
|
|
37
|
+
testId: string;
|
|
38
|
+
userId: string;
|
|
39
|
+
}>;
|
|
40
|
+
/**
|
|
41
|
+
* Test 5.1 - Crea test con metriche in transazione atomica
|
|
42
|
+
*/
|
|
43
|
+
createTestWithMetrics(testData: {
|
|
44
|
+
name: string;
|
|
45
|
+
startDate: string;
|
|
46
|
+
urls: string[];
|
|
47
|
+
}, metricsData: {
|
|
48
|
+
date: string;
|
|
49
|
+
clicks: number;
|
|
50
|
+
impressions: number;
|
|
51
|
+
}[], userId: string): Promise<{
|
|
52
|
+
testId: string;
|
|
53
|
+
metricsInserted: number;
|
|
54
|
+
statsUpdated: boolean;
|
|
55
|
+
}>;
|
|
56
|
+
/**
|
|
57
|
+
* Test 5.3 - Calcola media su grandi dataset
|
|
58
|
+
*/
|
|
59
|
+
calculateAverageMetrics(numberOfPages: number, numberOfDays: number): Promise<{
|
|
60
|
+
avgClicks: number;
|
|
61
|
+
avgImpressions: number;
|
|
62
|
+
rowsProcessed: number;
|
|
63
|
+
}>;
|
|
64
|
+
/**
|
|
65
|
+
* Test 5.3 - Query su range temporale con indici
|
|
66
|
+
*/
|
|
67
|
+
queryTimeRange(testId: string, startDate: string, endDate: string): Promise<{
|
|
68
|
+
rows: number;
|
|
69
|
+
useIndex: boolean;
|
|
70
|
+
indexName: string;
|
|
71
|
+
}>;
|
|
72
|
+
/**
|
|
73
|
+
* Test 5.3 - Calcola varianza su grandi dataset (puro TypeScript)
|
|
74
|
+
*/
|
|
75
|
+
calculateVariance(testId: string): Promise<{
|
|
76
|
+
variance: number;
|
|
77
|
+
stdDev: number;
|
|
78
|
+
rowsProcessed: number;
|
|
79
|
+
}>;
|
|
80
|
+
/**
|
|
81
|
+
* Test 5.3 - Paginazione risultati
|
|
82
|
+
*/
|
|
83
|
+
getMetricsPaginated(testId: string, page: number, pageSize: number): Promise<{
|
|
84
|
+
data: {
|
|
85
|
+
id: number;
|
|
86
|
+
testId: string;
|
|
87
|
+
date: string;
|
|
88
|
+
clicks: number;
|
|
89
|
+
impressions: number;
|
|
90
|
+
gapFilled: boolean;
|
|
91
|
+
filledAt: string | null;
|
|
92
|
+
createdAt: string;
|
|
93
|
+
}[];
|
|
94
|
+
total: number;
|
|
95
|
+
page: number;
|
|
96
|
+
hasMore: boolean;
|
|
97
|
+
}>;
|
|
98
|
+
/**
|
|
99
|
+
* Test 5.3 - Cleanup dati vecchi (retention policy)
|
|
100
|
+
*/
|
|
101
|
+
cleanupOldData(retentionYears: number): Promise<{
|
|
102
|
+
rowsDeleted: number;
|
|
103
|
+
oldestDateRemaining: string;
|
|
104
|
+
}>;
|
|
105
|
+
}
|
|
106
|
+
//# sourceMappingURL=DatabaseService.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DatabaseService.d.ts","sourceRoot":"","sources":["../../src/database/DatabaseService.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,EAAmB,KAAK,SAAS,EAAE,MAAM,SAAS,CAAC;AAG1D,qBAAa,eAAe;IAC1B,OAAO,CAAC,EAAE,CAAY;gBAEV,SAAS,CAAC,EAAE,SAAS;IAIjC;;OAEG;IACG,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;;;;;;;;;;;;;;;IAuBlD;;OAEG;IACG,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,EAAE,MAAM;;;;;;IAgBjF;;OAEG;IACG,qBAAqB,CACzB,QAAQ,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,EAAE,CAAA;KAAE,EAC7D,WAAW,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAA;KAAE,EAAE,EACpE,MAAM,EAAE,MAAM;;;;;IA+BhB;;OAEG;IACG,uBAAuB,CAAC,aAAa,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM;;;;;IAmBzE;;OAEG;IACG,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM;;;;;IAqBvE;;OAEG;IACG,iBAAiB,CAAC,MAAM,EAAE,MAAM;;;;;IAkBtC;;OAEG;IACG,mBAAmB,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM;;;;;;;;;;;;;;;IA4BxE;;OAEG;IACG,cAAc,CAAC,cAAc,EAAE,MAAM;;;;CAc5C"}
|