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.
Files changed (94) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +228 -0
  3. package/dist/auth/GoogleOAuthService.d.ts +31 -0
  4. package/dist/auth/GoogleOAuthService.d.ts.map +1 -0
  5. package/dist/auth/GoogleOAuthService.js +69 -0
  6. package/dist/auth/GoogleOAuthService.js.map +1 -0
  7. package/dist/auth/TokenManager.d.ts +56 -0
  8. package/dist/auth/TokenManager.d.ts.map +1 -0
  9. package/dist/auth/TokenManager.js +190 -0
  10. package/dist/auth/TokenManager.js.map +1 -0
  11. package/dist/cli/commands.d.ts +36 -0
  12. package/dist/cli/commands.d.ts.map +1 -0
  13. package/dist/cli/commands.js +471 -0
  14. package/dist/cli/commands.js.map +1 -0
  15. package/dist/cli/formatters.d.ts +24 -0
  16. package/dist/cli/formatters.d.ts.map +1 -0
  17. package/dist/cli/formatters.js +175 -0
  18. package/dist/cli/formatters.js.map +1 -0
  19. package/dist/cli.d.ts +15 -0
  20. package/dist/cli.d.ts.map +1 -0
  21. package/dist/cli.js +62 -0
  22. package/dist/cli.js.map +1 -0
  23. package/dist/config/AnalysisConfig.d.ts +13 -0
  24. package/dist/config/AnalysisConfig.d.ts.map +1 -0
  25. package/dist/config/AnalysisConfig.js +10 -0
  26. package/dist/config/AnalysisConfig.js.map +1 -0
  27. package/dist/config/env.d.ts +30 -0
  28. package/dist/config/env.d.ts.map +1 -0
  29. package/dist/config/env.js +77 -0
  30. package/dist/config/env.js.map +1 -0
  31. package/dist/database/DatabaseService.d.ts +106 -0
  32. package/dist/database/DatabaseService.d.ts.map +1 -0
  33. package/dist/database/DatabaseService.js +180 -0
  34. package/dist/database/DatabaseService.js.map +1 -0
  35. package/dist/database/TimeSeriesService.d.ts +53 -0
  36. package/dist/database/TimeSeriesService.d.ts.map +1 -0
  37. package/dist/database/TimeSeriesService.js +122 -0
  38. package/dist/database/TimeSeriesService.js.map +1 -0
  39. package/dist/database/db.d.ts +20 -0
  40. package/dist/database/db.d.ts.map +1 -0
  41. package/dist/database/db.js +60 -0
  42. package/dist/database/db.js.map +1 -0
  43. package/dist/database/schema.d.ts +687 -0
  44. package/dist/database/schema.d.ts.map +1 -0
  45. package/dist/database/schema.js +62 -0
  46. package/dist/database/schema.js.map +1 -0
  47. package/dist/demo.d.ts +13 -0
  48. package/dist/demo.d.ts.map +1 -0
  49. package/dist/demo.js +149 -0
  50. package/dist/demo.js.map +1 -0
  51. package/dist/gsc/GSCDataFetcher.d.ts +100 -0
  52. package/dist/gsc/GSCDataFetcher.d.ts.map +1 -0
  53. package/dist/gsc/GSCDataFetcher.js +398 -0
  54. package/dist/gsc/GSCDataFetcher.js.map +1 -0
  55. package/dist/gsc/GSCPermissionService.d.ts +20 -0
  56. package/dist/gsc/GSCPermissionService.d.ts.map +1 -0
  57. package/dist/gsc/GSCPermissionService.js +84 -0
  58. package/dist/gsc/GSCPermissionService.js.map +1 -0
  59. package/dist/index.d.ts +12 -0
  60. package/dist/index.d.ts.map +1 -0
  61. package/dist/index.js +41 -0
  62. package/dist/index.js.map +1 -0
  63. package/dist/notifications/NotificationService.d.ts +64 -0
  64. package/dist/notifications/NotificationService.d.ts.map +1 -0
  65. package/dist/notifications/NotificationService.js +115 -0
  66. package/dist/notifications/NotificationService.js.map +1 -0
  67. package/dist/orchestrator/SEOExperimentOrchestrator.d.ts +69 -0
  68. package/dist/orchestrator/SEOExperimentOrchestrator.d.ts.map +1 -0
  69. package/dist/orchestrator/SEOExperimentOrchestrator.js +199 -0
  70. package/dist/orchestrator/SEOExperimentOrchestrator.js.map +1 -0
  71. package/dist/services/ExportService.d.ts +52 -0
  72. package/dist/services/ExportService.d.ts.map +1 -0
  73. package/dist/services/ExportService.js +238 -0
  74. package/dist/services/ExportService.js.map +1 -0
  75. package/dist/smoke-test.d.ts +10 -0
  76. package/dist/smoke-test.d.ts.map +1 -0
  77. package/dist/smoke-test.js +73 -0
  78. package/dist/smoke-test.js.map +1 -0
  79. package/dist/stats/StatisticalEngine.d.ts +48 -0
  80. package/dist/stats/StatisticalEngine.d.ts.map +1 -0
  81. package/dist/stats/StatisticalEngine.js +205 -0
  82. package/dist/stats/StatisticalEngine.js.map +1 -0
  83. package/dist/stats/TDistribution.d.ts +28 -0
  84. package/dist/stats/TDistribution.d.ts.map +1 -0
  85. package/dist/stats/TDistribution.js +120 -0
  86. package/dist/stats/TDistribution.js.map +1 -0
  87. package/drizzle/0000_hot_whiplash.sql +51 -0
  88. package/drizzle/0001_open_photon.sql +9 -0
  89. package/drizzle/0002_faulty_the_watchers.sql +1 -0
  90. package/drizzle/meta/0000_snapshot.json +360 -0
  91. package/drizzle/meta/0001_snapshot.json +428 -0
  92. package/drizzle/meta/0002_snapshot.json +420 -0
  93. package/drizzle/meta/_journal.json +27 -0
  94. 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
@@ -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"}