@configjs/cli 1.1.16 → 1.1.18
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/dist/{angular-command-XN26G6L3.js → angular-command-EOREU45Q.js} +8 -8
- package/dist/{angular-installer-FY43HE72.js → angular-installer-TKZDPFLD.js} +9 -1
- package/dist/angular-setup-QDTWXOB4.js +30 -0
- package/dist/check-KAPRT4FM.js +168 -0
- package/dist/{chunk-JYWGJJ4M.js → chunk-D7IWYKUX.js} +476 -28
- package/dist/chunk-EDCNW4UO.js +92 -0
- package/dist/{chunk-TN27AX4L.js → chunk-FJLN62L4.js} +797 -18
- package/dist/{chunk-FIB2J36N.js → chunk-HI7RYD6W.js} +161 -36
- package/dist/{chunk-NYCK4R4K.js → chunk-RIJNUJDC.js} +361 -96
- package/dist/chunk-V2IBYLVH.js +932 -0
- package/dist/chunk-VN4XTFDK.js +315 -0
- package/dist/{chunk-UKEHW2LH.js → chunk-Y4XYC7QV.js} +17 -3
- package/dist/cli.js +31 -21
- package/dist/{installed-D6CUYQM5.js → installed-QMJZIZNC.js} +4 -4
- package/dist/{list-VZDUWV5O.js → list-5T6VDDAO.js} +4 -4
- package/dist/{nextjs-command-WKKOAY7I.js → nextjs-command-C6PM7A5C.js} +8 -9
- package/dist/{nextjs-installer-2ZC5IWJ6.js → nextjs-installer-OFY5BQC4.js} +9 -2
- package/dist/{nextjs-setup-DYOFF72S.js → nextjs-setup-JIKPIJCX.js} +21 -9
- package/dist/{react-command-2T6IOTHB.js → react-command-JMK6VM4Q.js} +8 -9
- package/dist/{remove-ZY3MLPGN.js → remove-4ZNQR6ZR.js} +4 -4
- package/dist/{svelte-command-B2DNNQ5Z.js → svelte-command-YUSD55NO.js} +8 -8
- package/dist/svelte-installer-UP3KDZSY.js +105 -0
- package/dist/{svelte-setup-FWXLXJAC.js → svelte-setup-33E46IBT.js} +16 -5
- package/dist/{vite-installer-Y6VMFHIM.js → vite-installer-EE2LE76G.js} +9 -2
- package/dist/{vite-setup-JRELX6K2.js → vite-setup-VO5BOI46.js} +16 -4
- package/dist/{vue-command-IOTC32AI.js → vue-command-3CYWLLFQ.js} +8 -9
- package/dist/{vue-installer-DGBBVF6F.js → vue-installer-LEGLVD77.js} +9 -2
- package/dist/{vue-setup-G44DLT2U.js → vue-setup-FK5QT7AY.js} +16 -4
- package/package.json +12 -4
- package/dist/angular-setup-Z6TCKNBG.js +0 -18
- package/dist/check-KNGZSCMM.js +0 -131
- package/dist/chunk-6GV4NKUX.js +0 -122
- package/dist/chunk-QPEUT7QG.js +0 -157
- package/dist/svelte-installer-EOSC3EP3.js +0 -65
|
@@ -1,24 +1,29 @@
|
|
|
1
1
|
import {
|
|
2
2
|
CompatibilityValidator,
|
|
3
3
|
allCompatibilityRules
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-RIJNUJDC.js";
|
|
5
5
|
import {
|
|
6
6
|
BackupManager,
|
|
7
7
|
ConfigWriter,
|
|
8
8
|
getPluginsByCategory,
|
|
9
9
|
getRecommendedPlugins,
|
|
10
10
|
pluginRegistry
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-D7IWYKUX.js";
|
|
12
12
|
import {
|
|
13
13
|
PluginTracker
|
|
14
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-Y4XYC7QV.js";
|
|
15
15
|
import {
|
|
16
16
|
SpinnerManager
|
|
17
17
|
} from "./chunk-KAMPBTFG.js";
|
|
18
|
+
import {
|
|
19
|
+
checkPathExists,
|
|
20
|
+
readFileContent,
|
|
21
|
+
writeFileContent
|
|
22
|
+
} from "./chunk-HI7RYD6W.js";
|
|
18
23
|
import {
|
|
19
24
|
getModuleLogger,
|
|
20
25
|
logger
|
|
21
|
-
} from "./chunk-
|
|
26
|
+
} from "./chunk-VN4XTFDK.js";
|
|
22
27
|
import {
|
|
23
28
|
getTranslations
|
|
24
29
|
} from "./chunk-L4GX22RG.js";
|
|
@@ -36,11 +41,11 @@ var frameworkRegistry = {
|
|
|
36
41
|
ts: "react-ts"
|
|
37
42
|
},
|
|
38
43
|
getSetupPrompt: async (language) => {
|
|
39
|
-
const { promptViteSetup } = await import("./vite-setup-
|
|
44
|
+
const { promptViteSetup } = await import("./vite-setup-VO5BOI46.js");
|
|
40
45
|
return await promptViteSetup(language);
|
|
41
46
|
},
|
|
42
47
|
createProject: async (options, currentDir, language) => {
|
|
43
|
-
const { createViteProject } = await import("./vite-installer-
|
|
48
|
+
const { createViteProject } = await import("./vite-installer-EE2LE76G.js");
|
|
44
49
|
return await createViteProject(
|
|
45
50
|
options,
|
|
46
51
|
currentDir,
|
|
@@ -60,11 +65,11 @@ var frameworkRegistry = {
|
|
|
60
65
|
defaultBundler: "nextjs",
|
|
61
66
|
createCommand: "npm create next-app@latest",
|
|
62
67
|
getSetupPrompt: async (language) => {
|
|
63
|
-
const { promptNextjsSetup } = await import("./nextjs-setup-
|
|
68
|
+
const { promptNextjsSetup } = await import("./nextjs-setup-JIKPIJCX.js");
|
|
64
69
|
return await promptNextjsSetup(language);
|
|
65
70
|
},
|
|
66
71
|
createProject: async (options, currentDir, language) => {
|
|
67
|
-
const { createNextjsProject } = await import("./nextjs-installer-
|
|
72
|
+
const { createNextjsProject } = await import("./nextjs-installer-OFY5BQC4.js");
|
|
68
73
|
return await createNextjsProject(
|
|
69
74
|
options,
|
|
70
75
|
currentDir,
|
|
@@ -88,11 +93,11 @@ var frameworkRegistry = {
|
|
|
88
93
|
ts: "vue-ts"
|
|
89
94
|
},
|
|
90
95
|
getSetupPrompt: async (language) => {
|
|
91
|
-
const { promptVueSetup } = await import("./vue-setup-
|
|
96
|
+
const { promptVueSetup } = await import("./vue-setup-FK5QT7AY.js");
|
|
92
97
|
return await promptVueSetup(language);
|
|
93
98
|
},
|
|
94
99
|
createProject: async (options, currentDir, language) => {
|
|
95
|
-
const { createVueProject } = await import("./vue-installer-
|
|
100
|
+
const { createVueProject } = await import("./vue-installer-LEGLVD77.js");
|
|
96
101
|
return await createVueProject(
|
|
97
102
|
options,
|
|
98
103
|
currentDir,
|
|
@@ -112,11 +117,11 @@ var frameworkRegistry = {
|
|
|
112
117
|
defaultBundler: "vite",
|
|
113
118
|
createCommand: "npm create svelte@latest",
|
|
114
119
|
getSetupPrompt: async (language) => {
|
|
115
|
-
const { promptSvelteSetup } = await import("./svelte-setup-
|
|
120
|
+
const { promptSvelteSetup } = await import("./svelte-setup-33E46IBT.js");
|
|
116
121
|
return await promptSvelteSetup(language);
|
|
117
122
|
},
|
|
118
123
|
createProject: async (options, currentDir, language) => {
|
|
119
|
-
const { createSvelteProject } = await import("./svelte-installer-
|
|
124
|
+
const { createSvelteProject } = await import("./svelte-installer-UP3KDZSY.js");
|
|
120
125
|
return await createSvelteProject(
|
|
121
126
|
options,
|
|
122
127
|
currentDir,
|
|
@@ -136,11 +141,11 @@ var frameworkRegistry = {
|
|
|
136
141
|
defaultBundler: "webpack",
|
|
137
142
|
createCommand: "npx @angular/cli@latest new",
|
|
138
143
|
getSetupPrompt: async () => {
|
|
139
|
-
const { promptAngularSetup } = await import("./angular-setup-
|
|
144
|
+
const { promptAngularSetup } = await import("./angular-setup-QDTWXOB4.js");
|
|
140
145
|
return await promptAngularSetup();
|
|
141
146
|
},
|
|
142
147
|
createProject: async (options, currentDir, language) => {
|
|
143
|
-
const { createAngularProject } = await import("./angular-installer-
|
|
148
|
+
const { createAngularProject } = await import("./angular-installer-TKZDPFLD.js");
|
|
144
149
|
return await createAngularProject(
|
|
145
150
|
options,
|
|
146
151
|
currentDir,
|
|
@@ -303,6 +308,669 @@ ${translations.confirmation.summary}
|
|
|
303
308
|
return confirm;
|
|
304
309
|
}
|
|
305
310
|
|
|
311
|
+
// src/core/snapshot-manager.ts
|
|
312
|
+
import { resolve } from "path";
|
|
313
|
+
var SnapshotManager = class {
|
|
314
|
+
/**
|
|
315
|
+
* @param projectRoot - Racine du projet
|
|
316
|
+
* @param fsAdapter - Adaptateur filesystem optionnel (pour tests)
|
|
317
|
+
*/
|
|
318
|
+
constructor(projectRoot, fsAdapter) {
|
|
319
|
+
this.projectRoot = projectRoot;
|
|
320
|
+
this.fsAdapter = fsAdapter;
|
|
321
|
+
this.startCleanupInterval();
|
|
322
|
+
}
|
|
323
|
+
logger = getModuleLogger();
|
|
324
|
+
/**
|
|
325
|
+
* Map des snapshots actifs: id -> snapshot
|
|
326
|
+
*/
|
|
327
|
+
snapshots = /* @__PURE__ */ new Map();
|
|
328
|
+
/**
|
|
329
|
+
* Interval ID pour le cleanup des snapshots expirés
|
|
330
|
+
*/
|
|
331
|
+
cleanupInterval;
|
|
332
|
+
/**
|
|
333
|
+
* TTL des snapshots en millisecondes (24 heures par défaut)
|
|
334
|
+
*/
|
|
335
|
+
snapshotTTL = 24 * 60 * 60 * 1e3;
|
|
336
|
+
/**
|
|
337
|
+
* Crée un snapshot de l'état actuel du projet
|
|
338
|
+
*
|
|
339
|
+
* Sauvegarde les fichiers critiques:
|
|
340
|
+
* - package.json
|
|
341
|
+
* - package-lock.json
|
|
342
|
+
* - yarn.lock / pnpm-lock.yaml
|
|
343
|
+
* - .npmrc / .yarnrc
|
|
344
|
+
*
|
|
345
|
+
* @param description - Description optionnelle du snapshot
|
|
346
|
+
* @returns ID du snapshot créé
|
|
347
|
+
* @throws {Error} Si le snapshot échoue
|
|
348
|
+
*
|
|
349
|
+
* @example
|
|
350
|
+
* ```typescript
|
|
351
|
+
* const snapshotId = await snapshotManager.createSnapshot('Before React installation')
|
|
352
|
+
* console.log('Created snapshot:', snapshotId)
|
|
353
|
+
* ```
|
|
354
|
+
*/
|
|
355
|
+
async createSnapshot(description) {
|
|
356
|
+
const snapshotId = this.generateSnapshotId();
|
|
357
|
+
const now = Date.now();
|
|
358
|
+
const files = /* @__PURE__ */ new Map();
|
|
359
|
+
this.logger.debug(`Creating snapshot ${snapshotId}`);
|
|
360
|
+
const criticalFiles = [
|
|
361
|
+
"package.json",
|
|
362
|
+
"package-lock.json",
|
|
363
|
+
"yarn.lock",
|
|
364
|
+
"pnpm-lock.yaml",
|
|
365
|
+
".npmrc",
|
|
366
|
+
".yarnrc",
|
|
367
|
+
".yarnrc.yml",
|
|
368
|
+
"tsconfig.json",
|
|
369
|
+
".eslintrc.json",
|
|
370
|
+
"vitest.config.ts"
|
|
371
|
+
];
|
|
372
|
+
for (const filename of criticalFiles) {
|
|
373
|
+
const filePath = resolve(this.projectRoot, filename);
|
|
374
|
+
try {
|
|
375
|
+
if (await checkPathExists(filePath, this.fsAdapter)) {
|
|
376
|
+
const content = await readFileContent(
|
|
377
|
+
filePath,
|
|
378
|
+
"utf-8",
|
|
379
|
+
this.fsAdapter
|
|
380
|
+
);
|
|
381
|
+
files.set(filePath, content);
|
|
382
|
+
this.logger.debug(`Snapshotted ${filename}`);
|
|
383
|
+
}
|
|
384
|
+
} catch (error) {
|
|
385
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
386
|
+
this.logger.debug(`Could not snapshot ${filename}: ${errorMessage}`);
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
const snapshot = {
|
|
390
|
+
id: snapshotId,
|
|
391
|
+
timestamp: now,
|
|
392
|
+
projectRoot: this.projectRoot,
|
|
393
|
+
files,
|
|
394
|
+
metadata: {
|
|
395
|
+
description,
|
|
396
|
+
createdAt: new Date(now).toISOString(),
|
|
397
|
+
expiresAt: new Date(now + this.snapshotTTL).toISOString()
|
|
398
|
+
}
|
|
399
|
+
};
|
|
400
|
+
this.snapshots.set(snapshotId, snapshot);
|
|
401
|
+
this.logger.info(
|
|
402
|
+
`Created snapshot ${snapshotId} with ${files.size} file(s)`
|
|
403
|
+
);
|
|
404
|
+
return snapshotId;
|
|
405
|
+
}
|
|
406
|
+
/**
|
|
407
|
+
* Restaure un projet depuis un snapshot
|
|
408
|
+
*
|
|
409
|
+
* Restore tous les fichiers sauvegardés dans le snapshot.
|
|
410
|
+
* Effectue une restauration atomique ou lance une erreur.
|
|
411
|
+
*
|
|
412
|
+
* @param snapshotId - ID du snapshot à restaurer
|
|
413
|
+
* @throws {Error} Si le snapshot n'existe pas ou si la restauration échoue
|
|
414
|
+
*
|
|
415
|
+
* @example
|
|
416
|
+
* ```typescript
|
|
417
|
+
* try {
|
|
418
|
+
* await snapshotManager.restoreSnapshot(snapshotId)
|
|
419
|
+
* console.log('Project restored successfully')
|
|
420
|
+
* } catch (error) {
|
|
421
|
+
* console.error('Restore failed:', error)
|
|
422
|
+
* }
|
|
423
|
+
* ```
|
|
424
|
+
*/
|
|
425
|
+
async restoreSnapshot(snapshotId) {
|
|
426
|
+
const snapshot = this.snapshots.get(snapshotId);
|
|
427
|
+
if (!snapshot) {
|
|
428
|
+
const availableIds = Array.from(this.snapshots.keys()).join(", ");
|
|
429
|
+
throw new Error(
|
|
430
|
+
`Snapshot not found: ${snapshotId}. Available: ${availableIds}`
|
|
431
|
+
);
|
|
432
|
+
}
|
|
433
|
+
this.logger.info(`Restoring snapshot ${snapshotId}`);
|
|
434
|
+
const errors = [];
|
|
435
|
+
for (const [filePath, content] of snapshot.files) {
|
|
436
|
+
try {
|
|
437
|
+
await writeFileContent(filePath, content, "utf-8", this.fsAdapter);
|
|
438
|
+
this.logger.debug(`Restored ${filePath}`);
|
|
439
|
+
} catch (error) {
|
|
440
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
441
|
+
const restoreError = new Error(
|
|
442
|
+
`Failed to restore ${filePath}: ${errorMessage}`
|
|
443
|
+
);
|
|
444
|
+
errors.push({ file: filePath, error: restoreError });
|
|
445
|
+
this.logger.error(errorMessage);
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
if (errors.length > 0) {
|
|
449
|
+
const errorMessages = errors.map((e) => `${e.file}: ${e.error.message}`).join("; ");
|
|
450
|
+
throw new Error(
|
|
451
|
+
`Restore failed for ${errors.length}/${snapshot.files.size} file(s): ${errorMessages}`
|
|
452
|
+
);
|
|
453
|
+
}
|
|
454
|
+
this.logger.info(
|
|
455
|
+
`Successfully restored snapshot ${snapshotId} (${snapshot.files.size} file(s))`
|
|
456
|
+
);
|
|
457
|
+
}
|
|
458
|
+
/**
|
|
459
|
+
* Libère (supprime) un snapshot
|
|
460
|
+
*
|
|
461
|
+
* À appeler après une installation réussie pour nettoyer les snapshots.
|
|
462
|
+
* Les snapshots sont aussi nettoyés automatiquement après 24h.
|
|
463
|
+
*
|
|
464
|
+
* @param snapshotId - ID du snapshot à libérer
|
|
465
|
+
* @returns true si le snapshot a été libéré, false s'il n'existait pas
|
|
466
|
+
*
|
|
467
|
+
* @example
|
|
468
|
+
* ```typescript
|
|
469
|
+
* await snapshotManager.releaseSnapshot(snapshotId)
|
|
470
|
+
* console.log('Snapshot cleaned up')
|
|
471
|
+
* ```
|
|
472
|
+
*/
|
|
473
|
+
releaseSnapshot(snapshotId) {
|
|
474
|
+
const removed = this.snapshots.delete(snapshotId);
|
|
475
|
+
if (removed) {
|
|
476
|
+
this.logger.info(`Released snapshot ${snapshotId}`);
|
|
477
|
+
} else {
|
|
478
|
+
this.logger.warn(`Snapshot ${snapshotId} not found for release`);
|
|
479
|
+
}
|
|
480
|
+
return removed;
|
|
481
|
+
}
|
|
482
|
+
/**
|
|
483
|
+
* Récupère les détails d'un snapshot
|
|
484
|
+
*
|
|
485
|
+
* @param snapshotId - ID du snapshot
|
|
486
|
+
* @returns Métadonnées du snapshot ou undefined
|
|
487
|
+
*
|
|
488
|
+
* @example
|
|
489
|
+
* ```typescript
|
|
490
|
+
* const info = snapshotManager.getSnapshotInfo(snapshotId)
|
|
491
|
+
* console.log('Created at:', info?.metadata.createdAt)
|
|
492
|
+
* ```
|
|
493
|
+
*/
|
|
494
|
+
getSnapshotInfo(snapshotId) {
|
|
495
|
+
return this.snapshots.get(snapshotId);
|
|
496
|
+
}
|
|
497
|
+
/**
|
|
498
|
+
* Liste tous les snapshots actifs
|
|
499
|
+
*
|
|
500
|
+
* @returns Array de métadonnées des snapshots
|
|
501
|
+
*
|
|
502
|
+
* @example
|
|
503
|
+
* ```typescript
|
|
504
|
+
* const snapshots = snapshotManager.listSnapshots()
|
|
505
|
+
* snapshots.forEach(s => {
|
|
506
|
+
* console.log(`${s.id}: ${s.metadata.description}`)
|
|
507
|
+
* })
|
|
508
|
+
* ```
|
|
509
|
+
*/
|
|
510
|
+
listSnapshots() {
|
|
511
|
+
return Array.from(this.snapshots.values());
|
|
512
|
+
}
|
|
513
|
+
/**
|
|
514
|
+
* Vérifie si un snapshot existe
|
|
515
|
+
*
|
|
516
|
+
* @param snapshotId - ID du snapshot
|
|
517
|
+
* @returns true si le snapshot existe, false sinon
|
|
518
|
+
*/
|
|
519
|
+
hasSnapshot(snapshotId) {
|
|
520
|
+
return this.snapshots.has(snapshotId);
|
|
521
|
+
}
|
|
522
|
+
/**
|
|
523
|
+
* Nettoie tous les snapshots expirés
|
|
524
|
+
*
|
|
525
|
+
* Appelé automatiquement toutes les heures.
|
|
526
|
+
* Peut aussi être appelé manuellement si nécessaire.
|
|
527
|
+
*
|
|
528
|
+
* @returns Nombre de snapshots nettoyés
|
|
529
|
+
*
|
|
530
|
+
* @example
|
|
531
|
+
* ```typescript
|
|
532
|
+
* const cleaned = snapshotManager.cleanupExpired()
|
|
533
|
+
* console.log(`Cleaned up ${cleaned} expired snapshot(s)`)
|
|
534
|
+
* ```
|
|
535
|
+
*/
|
|
536
|
+
cleanupExpired() {
|
|
537
|
+
const now = Date.now();
|
|
538
|
+
const toDelete = [];
|
|
539
|
+
for (const [id, snapshot] of this.snapshots) {
|
|
540
|
+
if (now > snapshot.timestamp + this.snapshotTTL) {
|
|
541
|
+
toDelete.push(id);
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
for (const id of toDelete) {
|
|
545
|
+
this.releaseSnapshot(id);
|
|
546
|
+
}
|
|
547
|
+
if (toDelete.length > 0) {
|
|
548
|
+
this.logger.info(`Cleaned up ${toDelete.length} expired snapshot(s)`);
|
|
549
|
+
}
|
|
550
|
+
return toDelete.length;
|
|
551
|
+
}
|
|
552
|
+
/**
|
|
553
|
+
* Nettoie TOUS les snapshots (utile pour reset complet)
|
|
554
|
+
*
|
|
555
|
+
* @returns Nombre de snapshots nettoyés
|
|
556
|
+
*
|
|
557
|
+
* @example
|
|
558
|
+
* ```typescript
|
|
559
|
+
* const count = snapshotManager.clearAll()
|
|
560
|
+
* console.log(`Cleared ${count} snapshot(s)`)
|
|
561
|
+
* ```
|
|
562
|
+
*/
|
|
563
|
+
clearAll() {
|
|
564
|
+
const count = this.snapshots.size;
|
|
565
|
+
this.snapshots.clear();
|
|
566
|
+
if (count > 0) {
|
|
567
|
+
this.logger.info(`Cleared all ${count} snapshot(s)`);
|
|
568
|
+
}
|
|
569
|
+
return count;
|
|
570
|
+
}
|
|
571
|
+
/**
|
|
572
|
+
* Arrête le cleanup interval (à appeler avant destruction)
|
|
573
|
+
*
|
|
574
|
+
* @example
|
|
575
|
+
* ```typescript
|
|
576
|
+
* snapshotManager.destroy()
|
|
577
|
+
* ```
|
|
578
|
+
*/
|
|
579
|
+
destroy() {
|
|
580
|
+
if (this.cleanupInterval) {
|
|
581
|
+
clearInterval(this.cleanupInterval);
|
|
582
|
+
this.cleanupInterval = void 0;
|
|
583
|
+
}
|
|
584
|
+
this.logger.debug("SnapshotManager destroyed");
|
|
585
|
+
}
|
|
586
|
+
/**
|
|
587
|
+
* Génère un ID unique pour snapshot
|
|
588
|
+
*
|
|
589
|
+
* @internal
|
|
590
|
+
*/
|
|
591
|
+
generateSnapshotId() {
|
|
592
|
+
const timestamp = Date.now();
|
|
593
|
+
const random = Math.random().toString(36).substring(2, 11);
|
|
594
|
+
return `snapshot-${timestamp}-${random}`;
|
|
595
|
+
}
|
|
596
|
+
/**
|
|
597
|
+
* Démarre le nettoyage automatique des snapshots expirés
|
|
598
|
+
*
|
|
599
|
+
* @internal
|
|
600
|
+
*/
|
|
601
|
+
startCleanupInterval() {
|
|
602
|
+
const interval = 60 * 60 * 1e3;
|
|
603
|
+
this.cleanupInterval = setInterval(() => {
|
|
604
|
+
this.cleanupExpired();
|
|
605
|
+
}, interval);
|
|
606
|
+
if (this.cleanupInterval.unref) {
|
|
607
|
+
this.cleanupInterval.unref();
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
};
|
|
611
|
+
|
|
612
|
+
// src/core/transaction-log.ts
|
|
613
|
+
var TransactionLog = class {
|
|
614
|
+
/**
|
|
615
|
+
* @param projectRoot - Racine du projet
|
|
616
|
+
*/
|
|
617
|
+
constructor(projectRoot) {
|
|
618
|
+
this.projectRoot = projectRoot;
|
|
619
|
+
}
|
|
620
|
+
logger = getModuleLogger();
|
|
621
|
+
/**
|
|
622
|
+
* Map des transactions actives: txId -> metadata + entries
|
|
623
|
+
*/
|
|
624
|
+
transactions = /* @__PURE__ */ new Map();
|
|
625
|
+
/**
|
|
626
|
+
* Transaction courante (pour logging convenience)
|
|
627
|
+
*/
|
|
628
|
+
currentTxId;
|
|
629
|
+
/**
|
|
630
|
+
* Counter pour IDs d'entries
|
|
631
|
+
*/
|
|
632
|
+
entryIdCounter = 0;
|
|
633
|
+
/**
|
|
634
|
+
* Démarre une nouvelle transaction
|
|
635
|
+
*
|
|
636
|
+
* @param plugins - Noms des plugins à installer
|
|
637
|
+
* @returns ID de la transaction
|
|
638
|
+
*
|
|
639
|
+
* @example
|
|
640
|
+
* ```typescript
|
|
641
|
+
* const txId = txLog.startTransaction(['react', 'redux'])
|
|
642
|
+
* ```
|
|
643
|
+
*/
|
|
644
|
+
startTransaction(plugins) {
|
|
645
|
+
const txId = this.generateTransactionId();
|
|
646
|
+
const now = Date.now();
|
|
647
|
+
const metadata = {
|
|
648
|
+
id: txId,
|
|
649
|
+
startTime: now,
|
|
650
|
+
projectRoot: this.projectRoot,
|
|
651
|
+
plugins
|
|
652
|
+
};
|
|
653
|
+
this.transactions.set(txId, {
|
|
654
|
+
metadata,
|
|
655
|
+
entries: []
|
|
656
|
+
});
|
|
657
|
+
this.currentTxId = txId;
|
|
658
|
+
this.logger.debug(`Started transaction ${txId}`);
|
|
659
|
+
return txId;
|
|
660
|
+
}
|
|
661
|
+
/**
|
|
662
|
+
* Enregistre une action dans la transaction courante
|
|
663
|
+
*
|
|
664
|
+
* @param action - Type d'action
|
|
665
|
+
* @param message - Message descriptif
|
|
666
|
+
* @param data - Données optionnelles
|
|
667
|
+
* @throws {Error} Si aucune transaction active
|
|
668
|
+
*
|
|
669
|
+
* @example
|
|
670
|
+
* ```typescript
|
|
671
|
+
* txLog.log(
|
|
672
|
+
* TransactionActionType.PACKAGE_INSTALL_START,
|
|
673
|
+
* 'Installing react',
|
|
674
|
+
* { package: 'react', version: '18.0.0' }
|
|
675
|
+
* )
|
|
676
|
+
* ```
|
|
677
|
+
*/
|
|
678
|
+
log(action, message, data) {
|
|
679
|
+
if (!this.currentTxId) {
|
|
680
|
+
throw new Error("No active transaction");
|
|
681
|
+
}
|
|
682
|
+
const tx = this.transactions.get(this.currentTxId);
|
|
683
|
+
if (!tx) {
|
|
684
|
+
throw new Error(`Transaction ${this.currentTxId} not found`);
|
|
685
|
+
}
|
|
686
|
+
const entry = {
|
|
687
|
+
id: this.generateEntryId(),
|
|
688
|
+
timestamp: Date.now(),
|
|
689
|
+
action,
|
|
690
|
+
message,
|
|
691
|
+
data
|
|
692
|
+
};
|
|
693
|
+
tx.entries.push(entry);
|
|
694
|
+
}
|
|
695
|
+
/**
|
|
696
|
+
* Enregistre une erreur dans la transaction courante
|
|
697
|
+
*
|
|
698
|
+
* @param message - Message d'erreur
|
|
699
|
+
* @param error - L'erreur originale
|
|
700
|
+
* @param data - Données optionnelles
|
|
701
|
+
* @throws {Error} Si aucune transaction active
|
|
702
|
+
*
|
|
703
|
+
* @example
|
|
704
|
+
* ```typescript
|
|
705
|
+
* try {
|
|
706
|
+
* // ... perform action ...
|
|
707
|
+
* } catch (error) {
|
|
708
|
+
* txLog.logError('Package install failed', error)
|
|
709
|
+
* }
|
|
710
|
+
* ```
|
|
711
|
+
*/
|
|
712
|
+
logError(message, error, data) {
|
|
713
|
+
if (!this.currentTxId) {
|
|
714
|
+
throw new Error("No active transaction");
|
|
715
|
+
}
|
|
716
|
+
const tx = this.transactions.get(this.currentTxId);
|
|
717
|
+
if (!tx) {
|
|
718
|
+
throw new Error(`Transaction ${this.currentTxId} not found`);
|
|
719
|
+
}
|
|
720
|
+
const entry = {
|
|
721
|
+
id: this.generateEntryId(),
|
|
722
|
+
timestamp: Date.now(),
|
|
723
|
+
action: "ERROR" /* ERROR */,
|
|
724
|
+
message,
|
|
725
|
+
data,
|
|
726
|
+
error: {
|
|
727
|
+
message: error.message,
|
|
728
|
+
stack: error.stack,
|
|
729
|
+
code: error.code
|
|
730
|
+
}
|
|
731
|
+
};
|
|
732
|
+
tx.entries.push(entry);
|
|
733
|
+
}
|
|
734
|
+
/**
|
|
735
|
+
* Enregistre un warning dans la transaction courante
|
|
736
|
+
*
|
|
737
|
+
* @param message - Message de warning
|
|
738
|
+
* @param data - Données optionnelles
|
|
739
|
+
* @throws {Error} Si aucune transaction active
|
|
740
|
+
*
|
|
741
|
+
* @example
|
|
742
|
+
* ```typescript
|
|
743
|
+
* txLog.logWarning('Plugin deprecation notice', {
|
|
744
|
+
* plugin: 'old-plugin',
|
|
745
|
+
* replacement: 'new-plugin'
|
|
746
|
+
* })
|
|
747
|
+
* ```
|
|
748
|
+
*/
|
|
749
|
+
logWarning(message, data) {
|
|
750
|
+
if (!this.currentTxId) {
|
|
751
|
+
throw new Error("No active transaction");
|
|
752
|
+
}
|
|
753
|
+
const tx = this.transactions.get(this.currentTxId);
|
|
754
|
+
if (!tx) {
|
|
755
|
+
throw new Error(`Transaction ${this.currentTxId} not found`);
|
|
756
|
+
}
|
|
757
|
+
const entry = {
|
|
758
|
+
id: this.generateEntryId(),
|
|
759
|
+
timestamp: Date.now(),
|
|
760
|
+
action: "WARNING" /* WARNING */,
|
|
761
|
+
message,
|
|
762
|
+
data
|
|
763
|
+
};
|
|
764
|
+
tx.entries.push(entry);
|
|
765
|
+
}
|
|
766
|
+
/**
|
|
767
|
+
* Enregistre une action avec timing (pour debugging performance)
|
|
768
|
+
*
|
|
769
|
+
* Utile pour mesurer la durée d'opérations comme install, validate, config.
|
|
770
|
+
*
|
|
771
|
+
* @param action - Type d'action
|
|
772
|
+
* @param message - Message descriptif
|
|
773
|
+
* @param durationMs - Durée en millisecondes
|
|
774
|
+
* @param data - Données optionnelles
|
|
775
|
+
* @throws {Error} Si aucune transaction active
|
|
776
|
+
*
|
|
777
|
+
* @example
|
|
778
|
+
* ```typescript
|
|
779
|
+
* const start = Date.now()
|
|
780
|
+
* // ... perform action ...
|
|
781
|
+
* txLog.logTimed(
|
|
782
|
+
* TransactionActionType.PACKAGE_INSTALL_COMPLETE,
|
|
783
|
+
* 'React installed',
|
|
784
|
+
* Date.now() - start,
|
|
785
|
+
* { packages: 1 }
|
|
786
|
+
* )
|
|
787
|
+
* ```
|
|
788
|
+
*/
|
|
789
|
+
logTimed(action, message, durationMs, data) {
|
|
790
|
+
if (!this.currentTxId) {
|
|
791
|
+
throw new Error("No active transaction");
|
|
792
|
+
}
|
|
793
|
+
const tx = this.transactions.get(this.currentTxId);
|
|
794
|
+
if (!tx) {
|
|
795
|
+
throw new Error(`Transaction ${this.currentTxId} not found`);
|
|
796
|
+
}
|
|
797
|
+
const entry = {
|
|
798
|
+
id: this.generateEntryId(),
|
|
799
|
+
timestamp: Date.now(),
|
|
800
|
+
action,
|
|
801
|
+
message,
|
|
802
|
+
data,
|
|
803
|
+
duration: durationMs
|
|
804
|
+
};
|
|
805
|
+
tx.entries.push(entry);
|
|
806
|
+
}
|
|
807
|
+
/**
|
|
808
|
+
* Termine la transaction courante
|
|
809
|
+
*
|
|
810
|
+
* @param success - Succès de la transaction
|
|
811
|
+
* @param snapshotId - ID du snapshot associé (optionnel)
|
|
812
|
+
* @throws {Error} Si aucune transaction active
|
|
813
|
+
*
|
|
814
|
+
* @example
|
|
815
|
+
* ```typescript
|
|
816
|
+
* txLog.endTransaction(true, snapshotId)
|
|
817
|
+
* ```
|
|
818
|
+
*/
|
|
819
|
+
endTransaction(success, snapshotId) {
|
|
820
|
+
if (!this.currentTxId) {
|
|
821
|
+
throw new Error("No active transaction");
|
|
822
|
+
}
|
|
823
|
+
const tx = this.transactions.get(this.currentTxId);
|
|
824
|
+
if (!tx) {
|
|
825
|
+
throw new Error(`Transaction ${this.currentTxId} not found`);
|
|
826
|
+
}
|
|
827
|
+
tx.metadata.endTime = Date.now();
|
|
828
|
+
tx.metadata.success = success;
|
|
829
|
+
tx.metadata.snapshotId = snapshotId;
|
|
830
|
+
const duration = (tx.metadata.endTime - tx.metadata.startTime) / 1e3;
|
|
831
|
+
this.logger.info(
|
|
832
|
+
`Transaction ${this.currentTxId} ${success ? "succeeded" : "failed"} in ${duration.toFixed(2)}s`
|
|
833
|
+
);
|
|
834
|
+
this.currentTxId = void 0;
|
|
835
|
+
}
|
|
836
|
+
/**
|
|
837
|
+
* Récupère le rapport complet d'une transaction
|
|
838
|
+
*
|
|
839
|
+
* @param txId - ID de la transaction
|
|
840
|
+
* @returns Rapport avec métadonnées et entries, ou undefined
|
|
841
|
+
*
|
|
842
|
+
* @example
|
|
843
|
+
* ```typescript
|
|
844
|
+
* const report = txLog.getReport(txId)
|
|
845
|
+
* console.log(`Transaction took ${report?.duration}ms`)
|
|
846
|
+
* console.log(`Total errors: ${report?.errorCount}`)
|
|
847
|
+
* ```
|
|
848
|
+
*/
|
|
849
|
+
getReport(txId) {
|
|
850
|
+
const tx = this.transactions.get(txId);
|
|
851
|
+
if (!tx) {
|
|
852
|
+
return void 0;
|
|
853
|
+
}
|
|
854
|
+
const duration = (tx.metadata.endTime ?? Date.now()) - tx.metadata.startTime;
|
|
855
|
+
const errorCount = tx.entries.filter(
|
|
856
|
+
(e) => e.action === "ERROR" /* ERROR */
|
|
857
|
+
).length;
|
|
858
|
+
const warningCount = tx.entries.filter(
|
|
859
|
+
(e) => e.action === "WARNING" /* WARNING */
|
|
860
|
+
).length;
|
|
861
|
+
return {
|
|
862
|
+
metadata: tx.metadata,
|
|
863
|
+
entries: tx.entries,
|
|
864
|
+
duration,
|
|
865
|
+
errorCount,
|
|
866
|
+
warningCount
|
|
867
|
+
};
|
|
868
|
+
}
|
|
869
|
+
/**
|
|
870
|
+
* Récupère les entries d'une transaction
|
|
871
|
+
*
|
|
872
|
+
* @param txId - ID de la transaction
|
|
873
|
+
* @returns Array d'entries, ou undefined si transaction pas trouvée
|
|
874
|
+
*
|
|
875
|
+
* @example
|
|
876
|
+
* ```typescript
|
|
877
|
+
* const entries = txLog.getEntries(txId)
|
|
878
|
+
* entries?.forEach(e => console.log(`[${e.action}] ${e.message}`))
|
|
879
|
+
* ```
|
|
880
|
+
*/
|
|
881
|
+
getEntries(txId) {
|
|
882
|
+
const tx = this.transactions.get(txId);
|
|
883
|
+
return tx?.entries;
|
|
884
|
+
}
|
|
885
|
+
/**
|
|
886
|
+
* Formate le rapport pour affichage/debugging
|
|
887
|
+
*
|
|
888
|
+
* @param txId - ID de la transaction
|
|
889
|
+
* @returns String formaté du rapport
|
|
890
|
+
*
|
|
891
|
+
* @example
|
|
892
|
+
* ```typescript
|
|
893
|
+
* console.log(txLog.formatReport(txId))
|
|
894
|
+
* ```
|
|
895
|
+
*/
|
|
896
|
+
formatReport(txId) {
|
|
897
|
+
const report = this.getReport(txId);
|
|
898
|
+
if (!report) {
|
|
899
|
+
return `Transaction ${txId} not found`;
|
|
900
|
+
}
|
|
901
|
+
const lines = [];
|
|
902
|
+
lines.push(`=== Transaction Report: ${txId} ===`);
|
|
903
|
+
lines.push(`Status: ${report.metadata.success ? "SUCCESS" : "FAILED"}`);
|
|
904
|
+
lines.push(`Duration: ${(report.duration / 1e3).toFixed(2)}s`);
|
|
905
|
+
lines.push(`Plugins: ${report.metadata.plugins.join(", ")}`);
|
|
906
|
+
lines.push(`Errors: ${report.errorCount}, Warnings: ${report.warningCount}`);
|
|
907
|
+
lines.push("");
|
|
908
|
+
lines.push("Timeline:");
|
|
909
|
+
for (const entry of report.entries) {
|
|
910
|
+
const time = new Date(entry.timestamp).toLocaleTimeString();
|
|
911
|
+
const duration = entry.duration ? ` (${entry.duration}ms)` : "";
|
|
912
|
+
lines.push(` [${time}] ${entry.action}${duration}: ${entry.message}`);
|
|
913
|
+
if (entry.error) {
|
|
914
|
+
lines.push(` Error: ${entry.error.message}`);
|
|
915
|
+
if (entry.error.code) {
|
|
916
|
+
lines.push(` Code: ${entry.error.code}`);
|
|
917
|
+
}
|
|
918
|
+
}
|
|
919
|
+
if (entry.data) {
|
|
920
|
+
lines.push(` Data: ${JSON.stringify(entry.data)}`);
|
|
921
|
+
}
|
|
922
|
+
}
|
|
923
|
+
return lines.join("\n");
|
|
924
|
+
}
|
|
925
|
+
/**
|
|
926
|
+
* Supprime une transaction du log
|
|
927
|
+
*
|
|
928
|
+
* @param txId - ID de la transaction
|
|
929
|
+
* @returns true si la transaction a été supprimée, false sinon
|
|
930
|
+
*
|
|
931
|
+
* @example
|
|
932
|
+
* ```typescript
|
|
933
|
+
* txLog.removeTransaction(txId)
|
|
934
|
+
* ```
|
|
935
|
+
*/
|
|
936
|
+
removeTransaction(txId) {
|
|
937
|
+
if (this.currentTxId === txId) {
|
|
938
|
+
this.currentTxId = void 0;
|
|
939
|
+
}
|
|
940
|
+
return this.transactions.delete(txId);
|
|
941
|
+
}
|
|
942
|
+
/**
|
|
943
|
+
* Nettoie tous les logs des transactions
|
|
944
|
+
*
|
|
945
|
+
* @example
|
|
946
|
+
* ```typescript
|
|
947
|
+
* txLog.clear()
|
|
948
|
+
* ```
|
|
949
|
+
*/
|
|
950
|
+
clear() {
|
|
951
|
+
this.transactions.clear();
|
|
952
|
+
this.currentTxId = void 0;
|
|
953
|
+
}
|
|
954
|
+
/**
|
|
955
|
+
* Génère un ID unique pour transaction
|
|
956
|
+
*
|
|
957
|
+
* @internal
|
|
958
|
+
*/
|
|
959
|
+
generateTransactionId() {
|
|
960
|
+
const timestamp = Date.now();
|
|
961
|
+
const random = Math.random().toString(36).substring(2, 11);
|
|
962
|
+
return `tx-${timestamp}-${random}`;
|
|
963
|
+
}
|
|
964
|
+
/**
|
|
965
|
+
* Génère un ID unique pour entry
|
|
966
|
+
*
|
|
967
|
+
* @internal
|
|
968
|
+
*/
|
|
969
|
+
generateEntryId() {
|
|
970
|
+
return `entry-${++this.entryIdCounter}`;
|
|
971
|
+
}
|
|
972
|
+
};
|
|
973
|
+
|
|
306
974
|
// src/core/installer.ts
|
|
307
975
|
var Installer = class {
|
|
308
976
|
/**
|
|
@@ -316,8 +984,12 @@ var Installer = class {
|
|
|
316
984
|
this.validator = validator;
|
|
317
985
|
this.backupManager = backupManager;
|
|
318
986
|
this.tracker = new PluginTracker(ctx.projectRoot, ctx.fsAdapter);
|
|
987
|
+
this.snapshotManager = new SnapshotManager(ctx.projectRoot, ctx.fsAdapter);
|
|
988
|
+
this.transactionLog = new TransactionLog(ctx.projectRoot);
|
|
319
989
|
}
|
|
320
990
|
tracker;
|
|
991
|
+
snapshotManager;
|
|
992
|
+
transactionLog;
|
|
321
993
|
logger = getModuleLogger();
|
|
322
994
|
/**
|
|
323
995
|
* Installe un ensemble de plugins
|
|
@@ -336,7 +1008,16 @@ var Installer = class {
|
|
|
336
1008
|
async install(plugins, options) {
|
|
337
1009
|
const startTime = Date.now();
|
|
338
1010
|
this.logger.info(`Starting installation of ${plugins.length} plugin(s)`);
|
|
1011
|
+
const txId = this.transactionLog.startTransaction(
|
|
1012
|
+
plugins.map((p) => p.name)
|
|
1013
|
+
);
|
|
1014
|
+
let snapshotId;
|
|
339
1015
|
try {
|
|
1016
|
+
this.logger.debug("PHASE 1: Validation (no modifications)");
|
|
1017
|
+
this.transactionLog.log(
|
|
1018
|
+
"VALIDATION_START" /* VALIDATION_START */,
|
|
1019
|
+
"Starting validation phase"
|
|
1020
|
+
);
|
|
340
1021
|
await this.tracker.load();
|
|
341
1022
|
const notInstalledPromises = plugins.map(async (p) => {
|
|
342
1023
|
const isTracked = this.tracker.isInstalled(p.name);
|
|
@@ -375,6 +1056,7 @@ var Installer = class {
|
|
|
375
1056
|
);
|
|
376
1057
|
if (notInstalled.length === 0) {
|
|
377
1058
|
this.logger.info("All plugins are already installed");
|
|
1059
|
+
this.transactionLog.endTransaction(true);
|
|
378
1060
|
return {
|
|
379
1061
|
success: true,
|
|
380
1062
|
duration: Date.now() - startTime,
|
|
@@ -405,8 +1087,19 @@ var Installer = class {
|
|
|
405
1087
|
`Found ${validationResult.warnings.length} warning(s):`,
|
|
406
1088
|
validationResult.warnings.map((w) => w.message)
|
|
407
1089
|
);
|
|
1090
|
+
for (const warning of validationResult.warnings) {
|
|
1091
|
+
this.transactionLog.logWarning(warning.message);
|
|
1092
|
+
}
|
|
408
1093
|
}
|
|
1094
|
+
this.transactionLog.log(
|
|
1095
|
+
"VALIDATION_COMPLETE" /* VALIDATION_COMPLETE */,
|
|
1096
|
+
`Validation passed for ${notInstalled.length} plugin(s)`
|
|
1097
|
+
);
|
|
409
1098
|
this.logger.debug("Resolving dependencies...");
|
|
1099
|
+
this.transactionLog.log(
|
|
1100
|
+
"DEPENDENCY_RESOLUTION" /* DEPENDENCY_RESOLUTION */,
|
|
1101
|
+
"Resolving plugin dependencies"
|
|
1102
|
+
);
|
|
410
1103
|
const resolved = this.resolveDependencies(notInstalled);
|
|
411
1104
|
const allPlugins = resolved.plugins;
|
|
412
1105
|
if (resolved.autoInstalled.length > 0) {
|
|
@@ -414,13 +1107,31 @@ var Installer = class {
|
|
|
414
1107
|
`Auto-installing ${resolved.autoInstalled.length} required dependency(ies): ${resolved.autoInstalled.join(", ")}`
|
|
415
1108
|
);
|
|
416
1109
|
}
|
|
1110
|
+
this.logger.debug("PHASE 2: Creating backup and snapshot");
|
|
1111
|
+
snapshotId = await this.snapshotManager.createSnapshot(
|
|
1112
|
+
`Before installing ${allPlugins.map((p) => p.displayName).join(", ")}`
|
|
1113
|
+
);
|
|
1114
|
+
this.transactionLog.log(
|
|
1115
|
+
"SNAPSHOT_CREATED" /* SNAPSHOT_CREATED */,
|
|
1116
|
+
`Snapshot created for rollback protection`,
|
|
1117
|
+
{ snapshotId }
|
|
1118
|
+
);
|
|
417
1119
|
this.logger.debug("Running pre-install hooks...");
|
|
1120
|
+
this.transactionLog.log(
|
|
1121
|
+
"PRE_INSTALL_HOOK" /* PRE_INSTALL_HOOK */,
|
|
1122
|
+
"Running pre-install hooks"
|
|
1123
|
+
);
|
|
418
1124
|
await this.runPreInstallHooks(allPlugins);
|
|
1125
|
+
this.logger.debug("PHASE 3: Installation");
|
|
419
1126
|
let installResults = [];
|
|
420
1127
|
if (options?.skipPackageInstall) {
|
|
421
1128
|
this.logger.info("Skipping package installation (--no-install mode)");
|
|
422
1129
|
} else {
|
|
423
1130
|
this.logger.debug("Installing packages...");
|
|
1131
|
+
this.transactionLog.log(
|
|
1132
|
+
"PACKAGE_INSTALL_START" /* PACKAGE_INSTALL_START */,
|
|
1133
|
+
"Starting package installation"
|
|
1134
|
+
);
|
|
424
1135
|
installResults = await this.installPackages(allPlugins);
|
|
425
1136
|
const failedInstalls = installResults.filter((r) => !r.success);
|
|
426
1137
|
if (failedInstalls.length > 0) {
|
|
@@ -428,6 +1139,10 @@ var Installer = class {
|
|
|
428
1139
|
`Failed to install packages for ${failedInstalls.length} plugin(s)`
|
|
429
1140
|
);
|
|
430
1141
|
}
|
|
1142
|
+
this.transactionLog.log(
|
|
1143
|
+
"PACKAGE_INSTALL_COMPLETE" /* PACKAGE_INSTALL_COMPLETE */,
|
|
1144
|
+
`Successfully installed packages for ${allPlugins.length} plugin(s)`
|
|
1145
|
+
);
|
|
431
1146
|
}
|
|
432
1147
|
this.logger.debug("Configuring plugins...");
|
|
433
1148
|
const configResults = [];
|
|
@@ -435,6 +1150,11 @@ var Installer = class {
|
|
|
435
1150
|
for (const plugin of allPlugins) {
|
|
436
1151
|
try {
|
|
437
1152
|
this.logger.debug(`Configuring ${plugin.displayName}...`);
|
|
1153
|
+
this.transactionLog.log(
|
|
1154
|
+
"PLUGIN_CONFIGURE_START" /* PLUGIN_CONFIGURE_START */,
|
|
1155
|
+
`Starting configuration for ${plugin.displayName}`,
|
|
1156
|
+
{ plugin: plugin.name }
|
|
1157
|
+
);
|
|
438
1158
|
const configResult = await plugin.configure(this.ctx);
|
|
439
1159
|
configResults.push(configResult);
|
|
440
1160
|
filesCreated.push(...configResult.files || []);
|
|
@@ -443,16 +1163,38 @@ var Installer = class {
|
|
|
443
1163
|
`Configuration failed for ${plugin.displayName}: ${configResult.message || "Unknown error"}`
|
|
444
1164
|
);
|
|
445
1165
|
}
|
|
1166
|
+
this.transactionLog.log(
|
|
1167
|
+
"PLUGIN_CONFIGURE_COMPLETE" /* PLUGIN_CONFIGURE_COMPLETE */,
|
|
1168
|
+
`Configuration complete for ${plugin.displayName}`,
|
|
1169
|
+
{
|
|
1170
|
+
plugin: plugin.name,
|
|
1171
|
+
filesCreated: configResult.files?.length ?? 0
|
|
1172
|
+
}
|
|
1173
|
+
);
|
|
446
1174
|
} catch (error) {
|
|
447
1175
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
448
1176
|
this.logger.error(
|
|
449
1177
|
`Configuration failed for ${plugin.displayName}: ${errorMessage}`
|
|
450
1178
|
);
|
|
1179
|
+
this.transactionLog.logError(
|
|
1180
|
+
`Configuration failed for ${plugin.displayName}`,
|
|
1181
|
+
error instanceof Error ? error : new Error(errorMessage),
|
|
1182
|
+
{ plugin: plugin.name }
|
|
1183
|
+
);
|
|
451
1184
|
throw error;
|
|
452
1185
|
}
|
|
453
1186
|
}
|
|
454
1187
|
this.logger.debug("Running post-install hooks...");
|
|
1188
|
+
this.transactionLog.log(
|
|
1189
|
+
"POST_INSTALL_HOOK" /* POST_INSTALL_HOOK */,
|
|
1190
|
+
"Running post-install hooks"
|
|
1191
|
+
);
|
|
455
1192
|
await this.runPostInstallHooks(allPlugins);
|
|
1193
|
+
this.logger.debug("PHASE 4: Cleanup on success");
|
|
1194
|
+
this.transactionLog.log(
|
|
1195
|
+
"CLEANUP_START" /* CLEANUP_START */,
|
|
1196
|
+
"Starting cleanup"
|
|
1197
|
+
);
|
|
456
1198
|
this.logger.debug("Tracking installed plugins...");
|
|
457
1199
|
const installResultsByName = /* @__PURE__ */ new Map();
|
|
458
1200
|
for (let i = 0; i < allPlugins.length; i++) {
|
|
@@ -475,11 +1217,19 @@ var Installer = class {
|
|
|
475
1217
|
}
|
|
476
1218
|
});
|
|
477
1219
|
}
|
|
1220
|
+
if (snapshotId) {
|
|
1221
|
+
this.snapshotManager.releaseSnapshot(snapshotId);
|
|
1222
|
+
}
|
|
1223
|
+
this.transactionLog.log(
|
|
1224
|
+
"CLEANUP_COMPLETE" /* CLEANUP_COMPLETE */,
|
|
1225
|
+
"Cleanup complete"
|
|
1226
|
+
);
|
|
478
1227
|
const duration = Date.now() - startTime;
|
|
479
1228
|
const installed = allPlugins.map((p) => p.name);
|
|
480
1229
|
this.logger.info(
|
|
481
1230
|
`Successfully installed ${installed.length} plugin(s) in ${duration}ms`
|
|
482
1231
|
);
|
|
1232
|
+
this.transactionLog.endTransaction(true, snapshotId);
|
|
483
1233
|
return {
|
|
484
1234
|
success: true,
|
|
485
1235
|
duration,
|
|
@@ -490,14 +1240,41 @@ var Installer = class {
|
|
|
490
1240
|
} catch (error) {
|
|
491
1241
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
492
1242
|
this.logger.error(`Installation failed: ${errorMessage}`);
|
|
493
|
-
this.
|
|
1243
|
+
this.transactionLog.logError(
|
|
1244
|
+
"Installation failed",
|
|
1245
|
+
error instanceof Error ? error : new Error(errorMessage)
|
|
1246
|
+
);
|
|
1247
|
+
this.logger.debug("ROLLBACK: Starting rollback procedure");
|
|
1248
|
+
this.transactionLog.log(
|
|
1249
|
+
"ROLLBACK_START" /* ROLLBACK_START */,
|
|
1250
|
+
"Starting rollback"
|
|
1251
|
+
);
|
|
494
1252
|
try {
|
|
1253
|
+
if (snapshotId && this.snapshotManager.hasSnapshot(snapshotId)) {
|
|
1254
|
+
this.logger.debug(
|
|
1255
|
+
`Restoring snapshot ${snapshotId} to previous state`
|
|
1256
|
+
);
|
|
1257
|
+
await this.snapshotManager.restoreSnapshot(snapshotId);
|
|
1258
|
+
this.logger.info("Snapshot restored successfully");
|
|
1259
|
+
}
|
|
495
1260
|
await this.rollback(plugins);
|
|
1261
|
+
this.transactionLog.log(
|
|
1262
|
+
"ROLLBACK_COMPLETE" /* ROLLBACK_COMPLETE */,
|
|
1263
|
+
"Rollback complete - snapshot retained for debugging"
|
|
1264
|
+
);
|
|
496
1265
|
} catch (rollbackError) {
|
|
497
1266
|
const rollbackMessage = rollbackError instanceof Error ? rollbackError.message : String(rollbackError);
|
|
498
1267
|
this.logger.error(`Rollback failed: ${rollbackMessage}`);
|
|
1268
|
+
this.transactionLog.logError(
|
|
1269
|
+
"Rollback procedure failed",
|
|
1270
|
+
rollbackError instanceof Error ? rollbackError : new Error(rollbackMessage)
|
|
1271
|
+
);
|
|
499
1272
|
}
|
|
500
1273
|
const duration = Date.now() - startTime;
|
|
1274
|
+
this.transactionLog.endTransaction(false, snapshotId);
|
|
1275
|
+
const txReport = this.transactionLog.formatReport(txId);
|
|
1276
|
+
this.logger.debug(`Transaction report:
|
|
1277
|
+
${txReport}`);
|
|
501
1278
|
return {
|
|
502
1279
|
success: false,
|
|
503
1280
|
duration,
|
|
@@ -505,6 +1282,8 @@ var Installer = class {
|
|
|
505
1282
|
warnings: [],
|
|
506
1283
|
filesCreated: []
|
|
507
1284
|
};
|
|
1285
|
+
} finally {
|
|
1286
|
+
this.snapshotManager.destroy();
|
|
508
1287
|
}
|
|
509
1288
|
}
|
|
510
1289
|
/**
|
|
@@ -654,7 +1433,7 @@ var Installer = class {
|
|
|
654
1433
|
};
|
|
655
1434
|
|
|
656
1435
|
// src/cli/ui/report.ts
|
|
657
|
-
import pc from "
|
|
1436
|
+
import pc from "chalk";
|
|
658
1437
|
function displayInstallationReport(report, plugins, lang) {
|
|
659
1438
|
const t = getTranslations(lang);
|
|
660
1439
|
console.log();
|
|
@@ -719,7 +1498,7 @@ function displayNextSteps(lang) {
|
|
|
719
1498
|
}
|
|
720
1499
|
|
|
721
1500
|
// src/cli/ui/logo.ts
|
|
722
|
-
import pc2 from "
|
|
1501
|
+
import pc2 from "chalk";
|
|
723
1502
|
function LOGO() {
|
|
724
1503
|
return [
|
|
725
1504
|
" \u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2557 \u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557",
|
|
@@ -735,7 +1514,7 @@ function displayLogo() {
|
|
|
735
1514
|
}
|
|
736
1515
|
|
|
737
1516
|
// src/cli/commands/base-framework-command.ts
|
|
738
|
-
import pc3 from "
|
|
1517
|
+
import pc3 from "chalk";
|
|
739
1518
|
var BaseFrameworkCommand = class {
|
|
740
1519
|
/**
|
|
741
1520
|
* Affiche les informations spécifiques du contexte détecté
|