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 @@
1
+ {"version":3,"file":"NotificationService.d.ts","sourceRoot":"","sources":["../../src/notifications/NotificationService.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,UAAU,UAAU;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,eAAe,EAAE,MAAM,CAAC;IACxB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,UAAU,UAAU;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,eAAe,EAAE;QACf,MAAM,EAAE,MAAM,CAAC;QACf,WAAW,EAAE,MAAM,CAAC;QACpB,eAAe,EAAE,MAAM,CAAC;KACzB,CAAC;CACH;AAED,UAAU,YAAY;IACpB,KAAK,EAAE,UAAU,EAAE,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;CACd;AAED,UAAU,UAAU;IAClB,IAAI,EAAE,OAAO,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,qBAAa,mBAAmB;IAC9B,OAAO,CAAC,iBAAiB,CAA0B;IAEnD;;OAEG;IACG,sBAAsB,CAAC,UAAU,EAAE,UAAU,GAAG,OAAO,CAAC,OAAO,CAAC;IActE;;OAEG;IACG,gBAAgB,CAAC,UAAU,EAAE,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC;IAgCnE;;OAEG;IACG,oBAAoB,CAAC,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,UAAU,EAAE,GAAG,OAAO,CAAC,YAAY,CAAC;IAiC7F;;OAEG;IACH,OAAO,CAAC,iBAAiB;IAuBzB;;OAEG;IACG,sBAAsB,CAAC,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,UAAU,EAAE,GAAG,OAAO,CAAC,OAAO,CAAC;CAI3F"}
@@ -0,0 +1,115 @@
1
+ /**
2
+ * NotificationService
3
+ *
4
+ * Gestisce l'invio di notifiche agli utenti:
5
+ * - Alert per test statisticamente significativi
6
+ * - Digest settimanali per utenti con test multipli
7
+ * - Prevenzione alert fatigue
8
+ */
9
+ export class NotificationService {
10
+ notificationsSent = new Set();
11
+ /**
12
+ * Test 6.1 - Determina se inviare alert vittoria
13
+ */
14
+ async shouldSendVictoryAlert(testResult) {
15
+ // Regola 1: p-value deve essere < 0.05 (statisticamente significativo)
16
+ if (testResult.pValue >= 0.05) {
17
+ return false;
18
+ }
19
+ // Regola 2: Miglioramento deve essere >= 5% (evita alert fatigue per miglioramenti minimi)
20
+ if (testResult.improvement < 0.05) {
21
+ return false;
22
+ }
23
+ return true;
24
+ }
25
+ /**
26
+ * Test 6.1 - Invia alert vittoria
27
+ */
28
+ async sendVictoryAlert(testResult) {
29
+ // Previeni invio duplicati
30
+ const notificationKey = `victory-${testResult.testId}`;
31
+ if (this.notificationsSent.has(notificationKey)) {
32
+ return {
33
+ sent: false,
34
+ reason: 'already_notified',
35
+ };
36
+ }
37
+ // Verifica se deve inviare
38
+ const shouldSend = await this.shouldSendVictoryAlert(testResult);
39
+ if (!shouldSend) {
40
+ return {
41
+ sent: false,
42
+ reason: 'not_significant',
43
+ };
44
+ }
45
+ // Marca come inviata
46
+ this.notificationsSent.add(notificationKey);
47
+ // Qui invieremmo realmente l'email
48
+ // await emailService.send(...)
49
+ return {
50
+ sent: true,
51
+ };
52
+ }
53
+ /**
54
+ * Test 6.2 - Genera digest settimanale
55
+ */
56
+ async generateWeeklyDigest(_userId, activeTests) {
57
+ // Ordina test per priorità (significativi prima)
58
+ const sortedTests = [...activeTests].sort((a, b) => {
59
+ // Test con p-value < 0.05 hanno priorità
60
+ const aSignificant = a.currentProgress.pValue < 0.05;
61
+ const bSignificant = b.currentProgress.pValue < 0.05;
62
+ if (aSignificant && !bSignificant)
63
+ return -1;
64
+ if (!aSignificant && bSignificant)
65
+ return 1;
66
+ // Altrimenti ordina per improvement
67
+ return b.currentProgress.improvement - a.currentProgress.improvement;
68
+ });
69
+ // Genera riepilogo complessivo
70
+ const significantTests = activeTests.filter(t => t.currentProgress.pValue < 0.05);
71
+ const summary = `${activeTests.length} test attivi. ${significantTests.length} statisticamente significativi.`;
72
+ // Genera body con sezioni per ogni test
73
+ let body = `Riepilogo Settimanale\n\n${summary}\n\n`;
74
+ for (const test of sortedTests) {
75
+ body += this.formatTestSection(test);
76
+ }
77
+ return {
78
+ tests: sortedTests,
79
+ userEmail: 'user@example.com', // TODO: recuperare email reale da userId
80
+ subject: 'Report Settimanale - I Tuoi Test SEO',
81
+ body,
82
+ };
83
+ }
84
+ /**
85
+ * Formatta sezione test nel digest
86
+ */
87
+ formatTestSection(test) {
88
+ const improvement = Math.round(test.currentProgress.improvement * 100);
89
+ const confidence = Math.round(test.currentProgress.confidenceLevel * 100);
90
+ let section = `\n--- ${test.testName} ---\n`;
91
+ section += `Durata: ${test.daysRunning} giorni\n`;
92
+ section += `Miglioramento: +${improvement}%\n`;
93
+ section += `Confidenza: ${confidence}%\n`;
94
+ // Azioni consigliate
95
+ if (test.currentProgress.pValue < 0.05) {
96
+ section += `✅ Azione: Pronto per applicare - Test statisticamente significativo!\n`;
97
+ }
98
+ else if (test.daysRunning < 10) {
99
+ section += `⏳ Azione: Continua a monitorare - Test ancora troppo giovane\n`;
100
+ }
101
+ else {
102
+ section += `👀 Azione: Continua a monitorare\n`;
103
+ }
104
+ section += '\n';
105
+ return section;
106
+ }
107
+ /**
108
+ * Test 6.2 - Determina se inviare digest settimanale
109
+ */
110
+ async shouldSendWeeklyDigest(_userId, activeTests) {
111
+ // NON inviare se non ci sono test attivi
112
+ return activeTests.length > 0;
113
+ }
114
+ }
115
+ //# sourceMappingURL=NotificationService.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"NotificationService.js","sourceRoot":"","sources":["../../src/notifications/NotificationService.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,69 @@
1
+ /**
2
+ * SEOExperimentOrchestrator
3
+ *
4
+ * Orchestra il flusso completo di un esperimento SEO:
5
+ * - Fetch dati da GSC tramite GSCDataFetcher
6
+ * - Salvataggio dati tramite TimeSeriesService
7
+ * - Analisi statistica tramite StatisticalEngine
8
+ * - Aggiornamento stato test nel database
9
+ * - Notifiche tramite NotificationService
10
+ * - Audit logging per tracciabilità
11
+ */
12
+ import { type DrizzleDB } from '../database/db.js';
13
+ import { GSCDataFetcher } from '../gsc/GSCDataFetcher.js';
14
+ import { StatisticalEngine } from '../stats/StatisticalEngine.js';
15
+ import { TimeSeriesService } from '../database/TimeSeriesService.js';
16
+ import { NotificationService } from '../notifications/NotificationService.js';
17
+ import { TokenManager } from '../auth/TokenManager.js';
18
+ export interface ExperimentResult {
19
+ testId: string;
20
+ status: 'updated' | 'completed' | 'insufficient_data' | 'error';
21
+ pValue: number | null;
22
+ percentageChange: number | null;
23
+ isSignificant: boolean;
24
+ message: string;
25
+ dataPointsBefore: number;
26
+ dataPointsAfter: number;
27
+ notificationSent: boolean;
28
+ }
29
+ export interface SyncResult {
30
+ totalTests: number;
31
+ successCount: number;
32
+ errorCount: number;
33
+ results: ExperimentResult[];
34
+ errors: Array<{
35
+ testId: string;
36
+ error: string;
37
+ }>;
38
+ }
39
+ export interface OrchestratorDeps {
40
+ db?: DrizzleDB;
41
+ gscFetcher?: GSCDataFetcher;
42
+ statisticalEngine?: StatisticalEngine;
43
+ timeSeriesService?: TimeSeriesService;
44
+ notificationService?: NotificationService;
45
+ tokenManager?: TokenManager;
46
+ }
47
+ export declare class SEOExperimentOrchestrator {
48
+ private db;
49
+ private gscFetcher;
50
+ private statisticalEngine;
51
+ private timeSeriesService;
52
+ private notificationService;
53
+ private tokenManager;
54
+ constructor(deps?: OrchestratorDeps);
55
+ /**
56
+ * Esegue il flusso completo per un singolo esperimento SEO.
57
+ */
58
+ runExperiment(testId: string): Promise<ExperimentResult>;
59
+ /**
60
+ * Sincronizza tutti i test con status 'running'.
61
+ * Resiliente: se un test fallisce, logga l'errore e continua.
62
+ */
63
+ syncAllActiveTests(): Promise<SyncResult>;
64
+ /**
65
+ * Aggiorna lo stato di un test e registra nel log.
66
+ */
67
+ private updateTestStatus;
68
+ }
69
+ //# sourceMappingURL=SEOExperimentOrchestrator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SEOExperimentOrchestrator.d.ts","sourceRoot":"","sources":["../../src/orchestrator/SEOExperimentOrchestrator.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAGH,OAAO,EAAmB,KAAK,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAEpE,OAAO,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAC1D,OAAO,EAAE,iBAAiB,EAAE,MAAM,+BAA+B,CAAC;AAClE,OAAO,EAAE,iBAAiB,EAAE,MAAM,kCAAkC,CAAC;AACrE,OAAO,EAAE,mBAAmB,EAAE,MAAM,yCAAyC,CAAC;AAC9E,OAAO,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AAEvD,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,SAAS,GAAG,WAAW,GAAG,mBAAmB,GAAG,OAAO,CAAC;IAChE,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,aAAa,EAAE,OAAO,CAAC;IACvB,OAAO,EAAE,MAAM,CAAC;IAChB,gBAAgB,EAAE,MAAM,CAAC;IACzB,eAAe,EAAE,MAAM,CAAC;IACxB,gBAAgB,EAAE,OAAO,CAAC;CAC3B;AAED,MAAM,WAAW,UAAU;IACzB,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,gBAAgB,EAAE,CAAC;IAC5B,MAAM,EAAE,KAAK,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAClD;AAED,MAAM,WAAW,gBAAgB;IAC/B,EAAE,CAAC,EAAE,SAAS,CAAC;IACf,UAAU,CAAC,EAAE,cAAc,CAAC;IAC5B,iBAAiB,CAAC,EAAE,iBAAiB,CAAC;IACtC,iBAAiB,CAAC,EAAE,iBAAiB,CAAC;IACtC,mBAAmB,CAAC,EAAE,mBAAmB,CAAC;IAC1C,YAAY,CAAC,EAAE,YAAY,CAAC;CAC7B;AAED,qBAAa,yBAAyB;IACpC,OAAO,CAAC,EAAE,CAAY;IACtB,OAAO,CAAC,UAAU,CAAiB;IACnC,OAAO,CAAC,iBAAiB,CAAoB;IAC7C,OAAO,CAAC,iBAAiB,CAAoB;IAC7C,OAAO,CAAC,mBAAmB,CAAsB;IACjD,OAAO,CAAC,YAAY,CAAe;gBAEvB,IAAI,CAAC,EAAE,gBAAgB;IASnC;;OAEG;IACG,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAsI9D;;;OAGG;IACG,kBAAkB,IAAI,OAAO,CAAC,UAAU,CAAC;IA6B/C;;OAEG;YACW,gBAAgB;CAgB/B"}
@@ -0,0 +1,199 @@
1
+ /**
2
+ * SEOExperimentOrchestrator
3
+ *
4
+ * Orchestra il flusso completo di un esperimento SEO:
5
+ * - Fetch dati da GSC tramite GSCDataFetcher
6
+ * - Salvataggio dati tramite TimeSeriesService
7
+ * - Analisi statistica tramite StatisticalEngine
8
+ * - Aggiornamento stato test nel database
9
+ * - Notifiche tramite NotificationService
10
+ * - Audit logging per tracciabilità
11
+ */
12
+ import { eq } from 'drizzle-orm';
13
+ import { db as defaultDb } from '../database/db.js';
14
+ import { tests, metrics, auditLogs, users } from '../database/schema.js';
15
+ import { GSCDataFetcher } from '../gsc/GSCDataFetcher.js';
16
+ import { StatisticalEngine } from '../stats/StatisticalEngine.js';
17
+ import { TimeSeriesService } from '../database/TimeSeriesService.js';
18
+ import { NotificationService } from '../notifications/NotificationService.js';
19
+ import { TokenManager } from '../auth/TokenManager.js';
20
+ export class SEOExperimentOrchestrator {
21
+ db;
22
+ gscFetcher;
23
+ statisticalEngine;
24
+ timeSeriesService;
25
+ notificationService;
26
+ tokenManager;
27
+ constructor(deps) {
28
+ this.db = deps?.db ?? defaultDb;
29
+ this.gscFetcher = deps?.gscFetcher ?? new GSCDataFetcher();
30
+ this.statisticalEngine = deps?.statisticalEngine ?? new StatisticalEngine();
31
+ this.timeSeriesService = deps?.timeSeriesService ?? new TimeSeriesService(deps?.db);
32
+ this.notificationService = deps?.notificationService ?? new NotificationService();
33
+ this.tokenManager = deps?.tokenManager ?? new TokenManager({ clientId: '', clientSecret: '' });
34
+ }
35
+ /**
36
+ * Esegue il flusso completo per un singolo esperimento SEO.
37
+ */
38
+ async runExperiment(testId) {
39
+ // 1. Carica test dal database
40
+ const test = this.db
41
+ .select()
42
+ .from(tests)
43
+ .where(eq(tests.id, testId))
44
+ .get();
45
+ if (!test) {
46
+ throw new Error(`Test ${testId} non trovato.`);
47
+ }
48
+ if (test.status !== 'running') {
49
+ throw new Error(`Test ${testId} non è in esecuzione (stato: ${test.status}).`);
50
+ }
51
+ // Carica user per email notifica
52
+ const user = this.db
53
+ .select()
54
+ .from(users)
55
+ .where(eq(users.id, test.userId))
56
+ .get();
57
+ // 2. Ottieni access token
58
+ const tokenResult = await this.tokenManager.getValidAccessToken(test.userId);
59
+ if (!tokenResult.success || !tokenResult.token) {
60
+ await this.updateTestStatus(testId, 'failed', test.userId);
61
+ throw new Error(`Token non valido per utente ${test.userId}: ${tokenResult.error}`);
62
+ }
63
+ // 3. Fetch nuovi dati da GSC
64
+ const today = new Date().toISOString().split('T')[0];
65
+ const startDateStr = test.startDate.split('T')[0];
66
+ const gscData = await this.gscFetcher.fetchSearchAnalytics(tokenResult.token, test.siteUrl, { startDate: startDateStr, endDate: today });
67
+ // 4. Salva nuovi dati nel database
68
+ if (gscData.rows && gscData.rows.length > 0) {
69
+ const timeSeriesData = gscData.rows.map((row) => ({
70
+ date: row.date || row.keys[0],
71
+ clicks: row.clicks,
72
+ impressions: row.impressions,
73
+ }));
74
+ await this.timeSeriesService.saveData(testId, timeSeriesData);
75
+ }
76
+ // 5. Ricarica tutte le metriche dal database (incluse le nuove)
77
+ const allMetrics = this.db
78
+ .select()
79
+ .from(metrics)
80
+ .where(eq(metrics.testId, testId))
81
+ .orderBy(metrics.date)
82
+ .all();
83
+ // 6. Dividi metriche in before/after alla splitDate
84
+ const splitDateStr = test.splitDate;
85
+ const beforeMetrics = allMetrics.filter((m) => m.date < splitDateStr);
86
+ const afterMetrics = allMetrics.filter((m) => m.date >= splitDateStr);
87
+ const beforeClicks = beforeMetrics.map((m) => m.clicks);
88
+ const afterClicks = afterMetrics.map((m) => m.clicks);
89
+ // 7. Esegui analisi statistica
90
+ const analysisResult = this.statisticalEngine.analyze({
91
+ before: beforeClicks,
92
+ after: afterClicks,
93
+ });
94
+ // 8. Determina nuovo stato
95
+ const newStatus = analysisResult.isSignificant ? 'completed' : 'running';
96
+ // 9. Aggiorna test nel database
97
+ this.db
98
+ .update(tests)
99
+ .set({
100
+ lastSyncAt: new Date().toISOString(),
101
+ lastPValue: analysisResult.pValue,
102
+ lastImprovement: analysisResult.percentageChange,
103
+ status: newStatus,
104
+ updatedAt: new Date().toISOString(),
105
+ })
106
+ .where(eq(tests.id, testId))
107
+ .run();
108
+ // 10. Registra nell'audit log
109
+ this.db
110
+ .insert(auditLogs)
111
+ .values({
112
+ testId,
113
+ action: 'experiment_synced',
114
+ userId: test.userId,
115
+ })
116
+ .run();
117
+ // 11. Gestisci notifiche (solo se significativo)
118
+ let notificationSent = false;
119
+ if (analysisResult.isSignificant && user?.email) {
120
+ const testResult = {
121
+ testId,
122
+ testName: test.name,
123
+ pValue: analysisResult.pValue,
124
+ improvement: analysisResult.percentageChange / 100,
125
+ confidenceLevel: 1 - analysisResult.pValue,
126
+ metricType: 'clicks',
127
+ userId: test.userId,
128
+ userEmail: user.email,
129
+ };
130
+ const sendResult = await this.notificationService.sendVictoryAlert(testResult);
131
+ notificationSent = sendResult.sent;
132
+ }
133
+ // 12. Restituisci risultato
134
+ return {
135
+ testId,
136
+ status: analysisResult.hasInsufficientData
137
+ ? 'insufficient_data'
138
+ : analysisResult.isSignificant
139
+ ? 'completed'
140
+ : 'updated',
141
+ pValue: analysisResult.pValue,
142
+ percentageChange: analysisResult.percentageChange,
143
+ isSignificant: analysisResult.isSignificant,
144
+ message: analysisResult.message,
145
+ dataPointsBefore: beforeClicks.length,
146
+ dataPointsAfter: afterClicks.length,
147
+ notificationSent,
148
+ };
149
+ }
150
+ /**
151
+ * Sincronizza tutti i test con status 'running'.
152
+ * Resiliente: se un test fallisce, logga l'errore e continua.
153
+ */
154
+ async syncAllActiveTests() {
155
+ const activeTests = this.db
156
+ .select()
157
+ .from(tests)
158
+ .where(eq(tests.status, 'running'))
159
+ .all();
160
+ const results = [];
161
+ const errors = [];
162
+ for (const test of activeTests) {
163
+ try {
164
+ const result = await this.runExperiment(test.id);
165
+ results.push(result);
166
+ }
167
+ catch (error) {
168
+ const errorMessage = error instanceof Error ? error.message : 'Errore sconosciuto';
169
+ errors.push({ testId: test.id, error: errorMessage });
170
+ }
171
+ }
172
+ return {
173
+ totalTests: activeTests.length,
174
+ successCount: results.length,
175
+ errorCount: errors.length,
176
+ results,
177
+ errors,
178
+ };
179
+ }
180
+ /**
181
+ * Aggiorna lo stato di un test e registra nel log.
182
+ */
183
+ async updateTestStatus(testId, status, userId) {
184
+ this.db
185
+ .update(tests)
186
+ .set({ status, updatedAt: new Date().toISOString() })
187
+ .where(eq(tests.id, testId))
188
+ .run();
189
+ this.db
190
+ .insert(auditLogs)
191
+ .values({
192
+ testId,
193
+ action: `status_changed_to_${status}`,
194
+ userId,
195
+ })
196
+ .run();
197
+ }
198
+ }
199
+ //# sourceMappingURL=SEOExperimentOrchestrator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SEOExperimentOrchestrator.js","sourceRoot":"","sources":["../../src/orchestrator/SEOExperimentOrchestrator.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,EAAE,EAAE,MAAM,aAAa,CAAC;AACjC,OAAO,EAAE,EAAE,IAAI,SAAS,EAAkB,MAAM,mBAAmB,CAAC;AACpE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,uBAAuB,CAAC;AACzE,OAAO,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAC1D,OAAO,EAAE,iBAAiB,EAAE,MAAM,+BAA+B,CAAC;AAClE,OAAO,EAAE,iBAAiB,EAAE,MAAM,kCAAkC,CAAC;AACrE,OAAO,EAAE,mBAAmB,EAAE,MAAM,yCAAyC,CAAC;AAC9E,OAAO,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AA+BvD,MAAM,OAAO,yBAAyB;IAC5B,EAAE,CAAY;IACd,UAAU,CAAiB;IAC3B,iBAAiB,CAAoB;IACrC,iBAAiB,CAAoB;IACrC,mBAAmB,CAAsB;IACzC,YAAY,CAAe;IAEnC,YAAY,IAAuB;QACjC,IAAI,CAAC,EAAE,GAAG,IAAI,EAAE,EAAE,IAAI,SAAS,CAAC;QAChC,IAAI,CAAC,UAAU,GAAG,IAAI,EAAE,UAAU,IAAI,IAAI,cAAc,EAAE,CAAC;QAC3D,IAAI,CAAC,iBAAiB,GAAG,IAAI,EAAE,iBAAiB,IAAI,IAAI,iBAAiB,EAAE,CAAC;QAC5E,IAAI,CAAC,iBAAiB,GAAG,IAAI,EAAE,iBAAiB,IAAI,IAAI,iBAAiB,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;QACpF,IAAI,CAAC,mBAAmB,GAAG,IAAI,EAAE,mBAAmB,IAAI,IAAI,mBAAmB,EAAE,CAAC;QAClF,IAAI,CAAC,YAAY,GAAG,IAAI,EAAE,YAAY,IAAI,IAAI,YAAY,CAAC,EAAE,QAAQ,EAAE,EAAE,EAAE,YAAY,EAAE,EAAE,EAAE,CAAC,CAAC;IACjG,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,aAAa,CAAC,MAAc;QAChC,8BAA8B;QAC9B,MAAM,IAAI,GAAG,IAAI,CAAC,EAAE;aACjB,MAAM,EAAE;aACR,IAAI,CAAC,KAAK,CAAC;aACX,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC,EAAE,EAAE,MAAM,CAAC,CAAC;aAC3B,GAAG,EAAE,CAAC;QAET,IAAI,CAAC,IAAI,EAAE,CAAC;YACV,MAAM,IAAI,KAAK,CAAC,QAAQ,MAAM,eAAe,CAAC,CAAC;QACjD,CAAC;QAED,IAAI,IAAI,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;YAC9B,MAAM,IAAI,KAAK,CAAC,QAAQ,MAAM,gCAAgC,IAAI,CAAC,MAAM,IAAI,CAAC,CAAC;QACjF,CAAC;QAED,iCAAiC;QACjC,MAAM,IAAI,GAAG,IAAI,CAAC,EAAE;aACjB,MAAM,EAAE;aACR,IAAI,CAAC,KAAK,CAAC;aACX,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC,EAAE,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC;aAChC,GAAG,EAAE,CAAC;QAET,0BAA0B;QAC1B,MAAM,WAAW,GAAG,MAAM,IAAI,CAAC,YAAY,CAAC,mBAAmB,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAC7E,IAAI,CAAC,WAAW,CAAC,OAAO,IAAI,CAAC,WAAW,CAAC,KAAK,EAAE,CAAC;YAC/C,MAAM,IAAI,CAAC,gBAAgB,CAAC,MAAM,EAAE,QAAQ,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC;YAC3D,MAAM,IAAI,KAAK,CAAC,+BAA+B,IAAI,CAAC,MAAM,KAAK,WAAW,CAAC,KAAK,EAAE,CAAC,CAAC;QACtF,CAAC;QAED,6BAA6B;QAC7B,MAAM,KAAK,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;QACrD,MAAM,YAAY,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;QAElD,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,UAAU,CAAC,oBAAoB,CACxD,WAAW,CAAC,KAAK,EACjB,IAAI,CAAC,OAAO,EACZ,EAAE,SAAS,EAAE,YAAY,EAAE,OAAO,EAAE,KAAK,EAAE,CAC5C,CAAC;QAEF,mCAAmC;QACnC,IAAI,OAAO,CAAC,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC5C,MAAM,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,GAAQ,EAAE,EAAE,CAAC,CAAC;gBACrD,IAAI,EAAE,GAAG,CAAC,IAAI,IAAI,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC;gBAC7B,MAAM,EAAE,GAAG,CAAC,MAAM;gBAClB,WAAW,EAAE,GAAG,CAAC,WAAW;aAC7B,CAAC,CAAC,CAAC;YACJ,MAAM,IAAI,CAAC,iBAAiB,CAAC,QAAQ,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;QAChE,CAAC;QAED,gEAAgE;QAChE,MAAM,UAAU,GAAG,IAAI,CAAC,EAAE;aACvB,MAAM,EAAE;aACR,IAAI,CAAC,OAAO,CAAC;aACb,KAAK,CAAC,EAAE,CAAC,OAAO,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;aACjC,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC;aACrB,GAAG,EAAE,CAAC;QAET,oDAAoD;QACpD,MAAM,YAAY,GAAG,IAAI,CAAC,SAAS,CAAC;QACpC,MAAM,aAAa,GAAG,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,GAAG,YAAY,CAAC,CAAC;QACtE,MAAM,YAAY,GAAG,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,IAAI,YAAY,CAAC,CAAC;QAEtE,MAAM,YAAY,GAAG,aAAa,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;QACxD,MAAM,WAAW,GAAG,YAAY,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;QAEtD,+BAA+B;QAC/B,MAAM,cAAc,GAAG,IAAI,CAAC,iBAAiB,CAAC,OAAO,CAAC;YACpD,MAAM,EAAE,YAAY;YACpB,KAAK,EAAE,WAAW;SACnB,CAAC,CAAC;QAEH,2BAA2B;QAC3B,MAAM,SAAS,GAAG,cAAc,CAAC,aAAa,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,SAAS,CAAC;QAEzE,gCAAgC;QAChC,IAAI,CAAC,EAAE;aACJ,MAAM,CAAC,KAAK,CAAC;aACb,GAAG,CAAC;YACH,UAAU,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;YACpC,UAAU,EAAE,cAAc,CAAC,MAAM;YACjC,eAAe,EAAE,cAAc,CAAC,gBAAgB;YAChD,MAAM,EAAE,SAAS;YACjB,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;SACpC,CAAC;aACD,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC,EAAE,EAAE,MAAM,CAAC,CAAC;aAC3B,GAAG,EAAE,CAAC;QAET,8BAA8B;QAC9B,IAAI,CAAC,EAAE;aACJ,MAAM,CAAC,SAAS,CAAC;aACjB,MAAM,CAAC;YACN,MAAM;YACN,MAAM,EAAE,mBAAmB;YAC3B,MAAM,EAAE,IAAI,CAAC,MAAM;SACpB,CAAC;aACD,GAAG,EAAE,CAAC;QAET,iDAAiD;QACjD,IAAI,gBAAgB,GAAG,KAAK,CAAC;QAC7B,IAAI,cAAc,CAAC,aAAa,IAAI,IAAI,EAAE,KAAK,EAAE,CAAC;YAChD,MAAM,UAAU,GAAG;gBACjB,MAAM;gBACN,QAAQ,EAAE,IAAI,CAAC,IAAI;gBACnB,MAAM,EAAE,cAAc,CAAC,MAAM;gBAC7B,WAAW,EAAE,cAAc,CAAC,gBAAgB,GAAG,GAAG;gBAClD,eAAe,EAAE,CAAC,GAAG,cAAc,CAAC,MAAM;gBAC1C,UAAU,EAAE,QAAQ;gBACpB,MAAM,EAAE,IAAI,CAAC,MAAM;gBACnB,SAAS,EAAE,IAAI,CAAC,KAAK;aACtB,CAAC;YAEF,MAAM,UAAU,GAAG,MAAM,IAAI,CAAC,mBAAmB,CAAC,gBAAgB,CAAC,UAAU,CAAC,CAAC;YAC/E,gBAAgB,GAAG,UAAU,CAAC,IAAI,CAAC;QACrC,CAAC;QAED,4BAA4B;QAC5B,OAAO;YACL,MAAM;YACN,MAAM,EAAE,cAAc,CAAC,mBAAmB;gBACxC,CAAC,CAAC,mBAAmB;gBACrB,CAAC,CAAC,cAAc,CAAC,aAAa;oBAC5B,CAAC,CAAC,WAAW;oBACb,CAAC,CAAC,SAAS;YACf,MAAM,EAAE,cAAc,CAAC,MAAM;YAC7B,gBAAgB,EAAE,cAAc,CAAC,gBAAgB;YACjD,aAAa,EAAE,cAAc,CAAC,aAAa;YAC3C,OAAO,EAAE,cAAc,CAAC,OAAO;YAC/B,gBAAgB,EAAE,YAAY,CAAC,MAAM;YACrC,eAAe,EAAE,WAAW,CAAC,MAAM;YACnC,gBAAgB;SACjB,CAAC;IACJ,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,kBAAkB;QACtB,MAAM,WAAW,GAAG,IAAI,CAAC,EAAE;aACxB,MAAM,EAAE;aACR,IAAI,CAAC,KAAK,CAAC;aACX,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;aAClC,GAAG,EAAE,CAAC;QAET,MAAM,OAAO,GAAuB,EAAE,CAAC;QACvC,MAAM,MAAM,GAA6C,EAAE,CAAC;QAE5D,KAAK,MAAM,IAAI,IAAI,WAAW,EAAE,CAAC;YAC/B,IAAI,CAAC;gBACH,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;gBACjD,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;YACvB,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,MAAM,YAAY,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,oBAAoB,CAAC;gBACnF,MAAM,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,EAAE,KAAK,EAAE,YAAY,EAAE,CAAC,CAAC;YACxD,CAAC;QACH,CAAC;QAED,OAAO;YACL,UAAU,EAAE,WAAW,CAAC,MAAM;YAC9B,YAAY,EAAE,OAAO,CAAC,MAAM;YAC5B,UAAU,EAAE,MAAM,CAAC,MAAM;YACzB,OAAO;YACP,MAAM;SACP,CAAC;IACJ,CAAC;IAED;;OAEG;IACK,KAAK,CAAC,gBAAgB,CAAC,MAAc,EAAE,MAAc,EAAE,MAAc;QAC3E,IAAI,CAAC,EAAE;aACJ,MAAM,CAAC,KAAK,CAAC;aACb,GAAG,CAAC,EAAE,MAAM,EAAE,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,EAAE,CAAC;aACpD,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC,EAAE,EAAE,MAAM,CAAC,CAAC;aAC3B,GAAG,EAAE,CAAC;QAET,IAAI,CAAC,EAAE;aACJ,MAAM,CAAC,SAAS,CAAC;aACjB,MAAM,CAAC;YACN,MAAM;YACN,MAAM,EAAE,qBAAqB,MAAM,EAAE;YACrC,MAAM;SACP,CAAC;aACD,GAAG,EAAE,CAAC;IACX,CAAC;CACF"}
@@ -0,0 +1,52 @@
1
+ /**
2
+ * ExportService
3
+ *
4
+ * Genera report professionali in formato Excel (.xlsx) e CSV
5
+ * a partire dai dati di un test SEO (metadata + metriche giornaliere).
6
+ */
7
+ export interface TestReportData {
8
+ id: string;
9
+ name: string;
10
+ siteUrl: string;
11
+ urls: string[];
12
+ startDate: string;
13
+ splitDate: string;
14
+ status: string;
15
+ lastPValue: number | null;
16
+ lastImprovement: number | null;
17
+ }
18
+ export interface MetricRow {
19
+ date: string;
20
+ clicks: number;
21
+ impressions: number;
22
+ gapFilled: boolean;
23
+ }
24
+ export interface ExportInput {
25
+ test: TestReportData;
26
+ metrics: MetricRow[];
27
+ }
28
+ export interface ExportResult {
29
+ buffer: Buffer;
30
+ fileName: string;
31
+ format: 'xlsx' | 'csv';
32
+ rowCount: number;
33
+ }
34
+ export declare class ReportExportService {
35
+ /**
36
+ * Genera un file Excel professionale con due fogli:
37
+ * - Riepilogo (metadata + analisi)
38
+ * - Dati Giornalieri (tabella metriche)
39
+ */
40
+ exportToExcel(input: ExportInput): Promise<ExportResult>;
41
+ /**
42
+ * Genera un file CSV piatto con le metriche giornaliere.
43
+ */
44
+ exportToCSV(input: ExportInput): Promise<ExportResult>;
45
+ private buildRiepilogoSheet;
46
+ private buildDatiSheet;
47
+ private addSectionHeader;
48
+ private addLabelValueRow;
49
+ buildFileName(testName: string, ext: 'xlsx' | 'csv'): string;
50
+ private formatDate;
51
+ }
52
+ //# sourceMappingURL=ExportService.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ExportService.d.ts","sourceRoot":"","sources":["../../src/services/ExportService.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAMH,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;CAChC;AAED,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,cAAc,CAAC;IACrB,OAAO,EAAE,SAAS,EAAE,CAAC;CACtB;AAED,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,GAAG,KAAK,CAAC;IACvB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAmBD,qBAAa,mBAAmB;IAC9B;;;;OAIG;IACG,aAAa,CAAC,KAAK,EAAE,WAAW,GAAG,OAAO,CAAC,YAAY,CAAC;IAqB9D;;OAEG;IACG,WAAW,CAAC,KAAK,EAAE,WAAW,GAAG,OAAO,CAAC,YAAY,CAAC;IA0B5D,OAAO,CAAC,mBAAmB;IAqG3B,OAAO,CAAC,cAAc;IAyDtB,OAAO,CAAC,gBAAgB;IAWxB,OAAO,CAAC,gBAAgB;IAkBxB,aAAa,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,KAAK,GAAG,MAAM;IAM5D,OAAO,CAAC,UAAU;CAWnB"}