@configjs/cli 1.1.4 → 1.1.5

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.
@@ -1,15 +1,200 @@
1
+ import {
2
+ detectPackageManager
3
+ } from "./chunk-MQV3WNMH.js";
1
4
  import {
2
5
  checkPathExists,
3
- detectPackageManager,
6
+ createDefaultFsAdapter,
4
7
  logger,
5
8
  readFileContent,
6
9
  readPackageJson,
7
10
  readTsConfig,
8
11
  writeFileContent
9
- } from "./chunk-QRFLHLFE.js";
12
+ } from "./chunk-HM2JWJOO.js";
13
+
14
+ // src/core/plugin-tracker.ts
15
+ import { join } from "path";
16
+ var CONFIG_FILE_NAME = ".configjsrc";
17
+ var CONFIG_VERSION = "1.0.0";
18
+ var PluginTracker = class {
19
+ configPath;
20
+ config = null;
21
+ fsAdapter;
22
+ constructor(projectRoot, fsAdapter) {
23
+ this.configPath = join(projectRoot, CONFIG_FILE_NAME);
24
+ this.fsAdapter = fsAdapter;
25
+ }
26
+ /**
27
+ * Charge la configuration depuis le fichier
28
+ */
29
+ async load() {
30
+ try {
31
+ const exists = await checkPathExists(this.configPath, this.fsAdapter);
32
+ if (!exists) {
33
+ this.config = {
34
+ version: CONFIG_VERSION,
35
+ installedPlugins: [],
36
+ lastUpdated: (/* @__PURE__ */ new Date()).toISOString()
37
+ };
38
+ return;
39
+ }
40
+ const content = await readFileContent(
41
+ this.configPath,
42
+ "utf-8",
43
+ this.fsAdapter
44
+ );
45
+ this.config = JSON.parse(content);
46
+ if (!this.config.version) {
47
+ logger.warn("Old config format detected, migrating...");
48
+ this.config.version = CONFIG_VERSION;
49
+ await this.save();
50
+ }
51
+ } catch (error) {
52
+ logger.error("Failed to load plugin configuration", error);
53
+ this.config = {
54
+ version: CONFIG_VERSION,
55
+ installedPlugins: [],
56
+ lastUpdated: (/* @__PURE__ */ new Date()).toISOString()
57
+ };
58
+ }
59
+ }
60
+ /**
61
+ * Sauvegarde la configuration dans le fichier
62
+ */
63
+ async save() {
64
+ if (!this.config) {
65
+ throw new Error("Configuration not loaded");
66
+ }
67
+ this.config.lastUpdated = (/* @__PURE__ */ new Date()).toISOString();
68
+ try {
69
+ const content = JSON.stringify(this.config, null, 2);
70
+ await writeFileContent(this.configPath, content, "utf-8", this.fsAdapter);
71
+ } catch (error) {
72
+ logger.error("Failed to save plugin configuration", error);
73
+ throw error;
74
+ }
75
+ }
76
+ /**
77
+ * Vérifie si un plugin est déjà installé
78
+ */
79
+ isInstalled(pluginName) {
80
+ if (!this.config) {
81
+ return false;
82
+ }
83
+ return this.config.installedPlugins.some((p) => p.name === pluginName);
84
+ }
85
+ /**
86
+ * Récupère les informations d'un plugin installé
87
+ */
88
+ getPlugin(pluginName) {
89
+ if (!this.config) {
90
+ return void 0;
91
+ }
92
+ return this.config.installedPlugins.find((p) => p.name === pluginName);
93
+ }
94
+ /**
95
+ * Récupère tous les plugins installés
96
+ */
97
+ getInstalledPlugins() {
98
+ return this.config?.installedPlugins || [];
99
+ }
100
+ /**
101
+ * Récupère tous les plugins installés d'une catégorie donnée
102
+ */
103
+ getPluginsByCategory(category) {
104
+ if (!this.config) {
105
+ return [];
106
+ }
107
+ return this.config.installedPlugins.filter((p) => p.category === category);
108
+ }
109
+ /**
110
+ * Ajoute un plugin à la liste des installés
111
+ */
112
+ async addPlugin(plugin) {
113
+ if (!this.config) {
114
+ throw new Error("Configuration not loaded");
115
+ }
116
+ if (this.isInstalled(plugin.name)) {
117
+ logger.warn(`Plugin ${plugin.name} is already marked as installed`);
118
+ return;
119
+ }
120
+ const installedPlugin = {
121
+ ...plugin,
122
+ installedAt: (/* @__PURE__ */ new Date()).toISOString()
123
+ };
124
+ this.config.installedPlugins.push(installedPlugin);
125
+ await this.save();
126
+ logger.debug(`Marked plugin ${plugin.name} as installed`);
127
+ }
128
+ /**
129
+ * Supprime un plugin de la liste des installés
130
+ */
131
+ async removePlugin(pluginName) {
132
+ if (!this.config) {
133
+ throw new Error("Configuration not loaded");
134
+ }
135
+ const index = this.config.installedPlugins.findIndex(
136
+ (p) => p.name === pluginName
137
+ );
138
+ if (index === -1) {
139
+ logger.warn(`Plugin ${pluginName} not found in installed plugins`);
140
+ return;
141
+ }
142
+ this.config.installedPlugins.splice(index, 1);
143
+ await this.save();
144
+ logger.debug(`Removed plugin ${pluginName} from installed plugins`);
145
+ }
146
+ /**
147
+ * Met à jour les informations d'un plugin installé
148
+ */
149
+ async updatePlugin(pluginName, updates) {
150
+ if (!this.config) {
151
+ throw new Error("Configuration not loaded");
152
+ }
153
+ const plugin = this.config.installedPlugins.find(
154
+ (p) => p.name === pluginName
155
+ );
156
+ if (!plugin) {
157
+ throw new Error(`Plugin ${pluginName} not found`);
158
+ }
159
+ Object.assign(plugin, updates);
160
+ await this.save();
161
+ logger.debug(`Updated plugin ${pluginName}`);
162
+ }
163
+ /**
164
+ * Efface toute la configuration
165
+ */
166
+ async reset() {
167
+ this.config = {
168
+ version: CONFIG_VERSION,
169
+ installedPlugins: [],
170
+ lastUpdated: (/* @__PURE__ */ new Date()).toISOString()
171
+ };
172
+ await this.save();
173
+ logger.info("Reset plugin configuration");
174
+ }
175
+ /**
176
+ * Vérifie les conflits potentiels avec un nouveau plugin
177
+ *
178
+ * @param category - Catégorie du plugin à installer
179
+ * @param exclusiveCategories - Catégories qui ne peuvent avoir qu'un seul plugin
180
+ * @returns Liste des plugins en conflit
181
+ */
182
+ checkCategoryConflicts(category, exclusiveCategories = ["routing", "state"]) {
183
+ if (!exclusiveCategories.includes(category)) {
184
+ return [];
185
+ }
186
+ return this.getPluginsByCategory(category);
187
+ }
188
+ /**
189
+ * Exporte la configuration pour affichage ou debug
190
+ */
191
+ export() {
192
+ return this.config;
193
+ }
194
+ };
10
195
 
11
196
  // src/core/detector.ts
12
- import { resolve, join } from "path";
197
+ import { resolve, join as join2 } from "path";
13
198
  import { platform, version } from "process";
14
199
  var detectionCache = /* @__PURE__ */ new Map();
15
200
  var DetectionError = class extends Error {
@@ -37,9 +222,17 @@ function detectFramework(pkg) {
37
222
  };
38
223
  }
39
224
  if (deps["vue"]) {
225
+ const version2 = deps["vue"].replace(/[\^~]/, "");
226
+ const majorVersion = parseInt(version2.split(".")[0] || "0", 10);
227
+ if (majorVersion < 3) {
228
+ throw new DetectionError(
229
+ "Vue 2 is not supported. Please use Vue 3 (Vue 2 is not compatible with Vite).",
230
+ { vueVersion: version2 }
231
+ );
232
+ }
40
233
  return {
41
234
  framework: "vue",
42
- version: deps["vue"].replace(/[\^~]/, "")
235
+ version: version2
43
236
  };
44
237
  }
45
238
  if (deps["svelte"]) {
@@ -53,13 +246,16 @@ function detectFramework(pkg) {
53
246
  { dependencies: Object.keys(deps) }
54
247
  );
55
248
  }
56
- async function detectBundler(projectRoot, pkg) {
249
+ async function detectBundler(projectRoot, pkg, fsAdapter) {
57
250
  const deps = {
58
251
  ...pkg["dependencies"] || {},
59
252
  ...pkg["devDependencies"] || {}
60
253
  };
61
254
  if (deps["next"]) {
62
- const nextConfigExists = await checkPathExists(join(projectRoot, "next.config.js")) || await checkPathExists(join(projectRoot, "next.config.ts")) || await checkPathExists(join(projectRoot, "next.config.mjs")) || await checkPathExists(join(projectRoot, "next.config.cjs"));
255
+ const nextConfigExists = await checkPathExists(join2(projectRoot, "next.config.js"), fsAdapter) || await checkPathExists(join2(projectRoot, "next.config.ts"), fsAdapter) || await checkPathExists(
256
+ join2(projectRoot, "next.config.mjs"),
257
+ fsAdapter
258
+ ) || await checkPathExists(join2(projectRoot, "next.config.cjs"), fsAdapter);
63
259
  if (nextConfigExists) {
64
260
  return {
65
261
  bundler: "nextjs",
@@ -68,7 +264,10 @@ async function detectBundler(projectRoot, pkg) {
68
264
  }
69
265
  }
70
266
  if (deps["vite"]) {
71
- const viteConfigExists = await checkPathExists(join(projectRoot, "vite.config.js")) || await checkPathExists(join(projectRoot, "vite.config.ts")) || await checkPathExists(join(projectRoot, "vite.config.mjs")) || await checkPathExists(join(projectRoot, "vite.config.cjs"));
267
+ const viteConfigExists = await checkPathExists(join2(projectRoot, "vite.config.js"), fsAdapter) || await checkPathExists(join2(projectRoot, "vite.config.ts"), fsAdapter) || await checkPathExists(
268
+ join2(projectRoot, "vite.config.mjs"),
269
+ fsAdapter
270
+ ) || await checkPathExists(join2(projectRoot, "vite.config.cjs"), fsAdapter);
72
271
  if (viteConfigExists) {
73
272
  return {
74
273
  bundler: "vite",
@@ -76,6 +275,19 @@ async function detectBundler(projectRoot, pkg) {
76
275
  };
77
276
  }
78
277
  }
278
+ if (deps["vue"] && deps["@vue/cli-service"]) {
279
+ const vueConfigExists = await checkPathExists(
280
+ join2(projectRoot, "vue.config.js"),
281
+ fsAdapter
282
+ );
283
+ if (vueConfigExists) {
284
+ return {
285
+ bundler: "webpack",
286
+ // Vue CLI utilise Webpack
287
+ version: deps["@vue/cli-service"].replace(/[\^~]/, "")
288
+ };
289
+ }
290
+ }
79
291
  if (deps["react-scripts"]) {
80
292
  return {
81
293
  bundler: "cra",
@@ -83,7 +295,10 @@ async function detectBundler(projectRoot, pkg) {
83
295
  };
84
296
  }
85
297
  if (deps["webpack"]) {
86
- const webpackConfigExists = await checkPathExists(join(projectRoot, "webpack.config.js")) || await checkPathExists(join(projectRoot, "webpack.config.ts"));
298
+ const webpackConfigExists = await checkPathExists(
299
+ join2(projectRoot, "webpack.config.js"),
300
+ fsAdapter
301
+ ) || await checkPathExists(join2(projectRoot, "webpack.config.ts"), fsAdapter);
87
302
  if (webpackConfigExists) {
88
303
  return {
89
304
  bundler: "webpack",
@@ -102,16 +317,16 @@ async function detectBundler(projectRoot, pkg) {
102
317
  version: null
103
318
  };
104
319
  }
105
- async function detectTypeScript(projectRoot) {
106
- const tsConfig = await readTsConfig(projectRoot);
320
+ async function detectTypeScript(projectRoot, fsAdapter) {
321
+ const tsConfig = await readTsConfig(projectRoot, fsAdapter);
107
322
  if (tsConfig) {
108
323
  const possiblePaths = [
109
- join(projectRoot, "tsconfig.json"),
110
- join(projectRoot, "tsconfig.app.json"),
111
- join(projectRoot, "tsconfig.node.json")
324
+ join2(projectRoot, "tsconfig.json"),
325
+ join2(projectRoot, "tsconfig.app.json"),
326
+ join2(projectRoot, "tsconfig.node.json")
112
327
  ];
113
328
  for (const path of possiblePaths) {
114
- if (await checkPathExists(path)) {
329
+ if (await checkPathExists(path, fsAdapter)) {
115
330
  return {
116
331
  typescript: true,
117
332
  tsconfigPath: path
@@ -120,40 +335,40 @@ async function detectTypeScript(projectRoot) {
120
335
  }
121
336
  return {
122
337
  typescript: true,
123
- tsconfigPath: join(projectRoot, "tsconfig.json")
338
+ tsconfigPath: join2(projectRoot, "tsconfig.json")
124
339
  };
125
340
  }
126
341
  return {
127
342
  typescript: false
128
343
  };
129
344
  }
130
- async function detectSrcDir(projectRoot) {
345
+ async function detectSrcDir(projectRoot, fsAdapter) {
131
346
  const possibleDirs = ["src", "app", "source", "lib"];
132
347
  for (const dir of possibleDirs) {
133
- const dirPath = join(projectRoot, dir);
134
- if (await checkPathExists(dirPath)) {
348
+ const dirPath = join2(projectRoot, dir);
349
+ if (await checkPathExists(dirPath, fsAdapter)) {
135
350
  return dir;
136
351
  }
137
352
  }
138
353
  return "src";
139
354
  }
140
- async function detectPublicDir(projectRoot) {
355
+ async function detectPublicDir(projectRoot, fsAdapter) {
141
356
  const possibleDirs = ["public", "static", "assets"];
142
357
  for (const dir of possibleDirs) {
143
- const dirPath = join(projectRoot, dir);
144
- if (await checkPathExists(dirPath)) {
358
+ const dirPath = join2(projectRoot, dir);
359
+ if (await checkPathExists(dirPath, fsAdapter)) {
145
360
  return dir;
146
361
  }
147
362
  }
148
363
  return "public";
149
364
  }
150
- async function detectNextjsRouter(projectRoot, srcDir) {
151
- const appDirInSrc = join(projectRoot, srcDir, "app");
152
- const pagesDirInSrc = join(projectRoot, srcDir, "pages");
153
- const appDirAtRoot = join(projectRoot, "app");
154
- const pagesDirAtRoot = join(projectRoot, "pages");
155
- const appDirExists = await checkPathExists(appDirInSrc) || await checkPathExists(appDirAtRoot);
156
- const pagesDirExists = await checkPathExists(pagesDirInSrc) || await checkPathExists(pagesDirAtRoot);
365
+ async function detectNextjsRouter(projectRoot, srcDir, fsAdapter) {
366
+ const appDirInSrc = join2(projectRoot, srcDir, "app");
367
+ const pagesDirInSrc = join2(projectRoot, srcDir, "pages");
368
+ const appDirAtRoot = join2(projectRoot, "app");
369
+ const pagesDirAtRoot = join2(projectRoot, "pages");
370
+ const appDirExists = await checkPathExists(appDirInSrc, fsAdapter) || await checkPathExists(appDirAtRoot, fsAdapter);
371
+ const pagesDirExists = await checkPathExists(pagesDirInSrc, fsAdapter) || await checkPathExists(pagesDirAtRoot, fsAdapter);
157
372
  if (appDirExists) {
158
373
  return "app";
159
374
  }
@@ -162,20 +377,66 @@ async function detectNextjsRouter(projectRoot, srcDir) {
162
377
  }
163
378
  return void 0;
164
379
  }
165
- async function detectGit(projectRoot) {
166
- const gitDir = join(projectRoot, ".git");
167
- const hasGit = await checkPathExists(gitDir);
380
+ async function detectVueApi(projectRoot, srcDir, fsAdapter) {
381
+ const adapter = fsAdapter || createDefaultFsAdapter();
382
+ const srcPath = join2(projectRoot, srcDir);
383
+ if (!await checkPathExists(srcPath, adapter)) {
384
+ return void 0;
385
+ }
386
+ try {
387
+ const files = [];
388
+ async function findVueFiles(dir) {
389
+ const entries = await adapter.readdir(dir);
390
+ for (const entryName of entries) {
391
+ const fullPath = join2(dir, entryName);
392
+ const stat = await adapter.stat(fullPath);
393
+ if (stat.isDirectory()) {
394
+ await findVueFiles(fullPath);
395
+ } else if (entryName.endsWith(".vue")) {
396
+ files.push(fullPath);
397
+ }
398
+ }
399
+ }
400
+ await findVueFiles(srcPath);
401
+ let hasCompositionApi = false;
402
+ let hasOptionsApi = false;
403
+ for (const file of files.slice(0, 10)) {
404
+ try {
405
+ const content = await adapter.readFile(file, "utf-8");
406
+ if (content.includes("<script setup>") || content.includes('<script setup lang="ts">') || content.includes("export default defineComponent") || content.includes("setup()")) {
407
+ hasCompositionApi = true;
408
+ }
409
+ if (content.includes("export default {") || content.includes("export default{")) {
410
+ hasOptionsApi = true;
411
+ }
412
+ } catch {
413
+ }
414
+ }
415
+ if (hasCompositionApi) {
416
+ return "composition";
417
+ }
418
+ if (hasOptionsApi) {
419
+ return "options";
420
+ }
421
+ return void 0;
422
+ } catch {
423
+ return void 0;
424
+ }
425
+ }
426
+ async function detectGit(projectRoot, fsAdapter) {
427
+ const gitDir = join2(projectRoot, ".git");
428
+ const hasGit = await checkPathExists(gitDir, fsAdapter);
168
429
  if (!hasGit) {
169
430
  return { hasGit: false };
170
431
  }
171
- const hooksPath = join(gitDir, "hooks");
172
- const hasHooks = await checkPathExists(hooksPath);
432
+ const hooksPath = join2(gitDir, "hooks");
433
+ const hasHooks = await checkPathExists(hooksPath, fsAdapter);
173
434
  return {
174
435
  hasGit: true,
175
436
  gitHooksPath: hasHooks ? hooksPath : void 0
176
437
  };
177
438
  }
178
- async function detectLockfile(projectRoot, packageManager) {
439
+ async function detectLockfile(projectRoot, packageManager, fsAdapter) {
179
440
  const lockfiles = {
180
441
  npm: "package-lock.json",
181
442
  yarn: "yarn.lock",
@@ -183,13 +444,13 @@ async function detectLockfile(projectRoot, packageManager) {
183
444
  bun: "bun.lockb"
184
445
  };
185
446
  const lockfile = lockfiles[packageManager];
186
- const lockfilePath = join(projectRoot, lockfile);
187
- if (await checkPathExists(lockfilePath)) {
447
+ const lockfilePath = join2(projectRoot, lockfile);
448
+ if (await checkPathExists(lockfilePath, fsAdapter)) {
188
449
  return lockfile;
189
450
  }
190
451
  return lockfile;
191
452
  }
192
- async function detectContext(projectRoot) {
453
+ async function detectContext(projectRoot, fsAdapter) {
193
454
  const fullPath = resolve(projectRoot);
194
455
  if (detectionCache.has(fullPath)) {
195
456
  logger.debug(`Using cached context for ${fullPath}`);
@@ -201,7 +462,7 @@ async function detectContext(projectRoot) {
201
462
  logger.debug(`Detecting context for project: ${fullPath}`);
202
463
  let pkg;
203
464
  try {
204
- pkg = await readPackageJson(fullPath);
465
+ pkg = await readPackageJson(fullPath, fsAdapter);
205
466
  } catch (error) {
206
467
  const errorMessage = error instanceof Error ? error.message : String(error);
207
468
  throw new DetectionError(
@@ -219,15 +480,17 @@ async function detectContext(projectRoot) {
219
480
  gitInfo
220
481
  ] = await Promise.all([
221
482
  Promise.resolve(detectFramework(pkg)),
222
- detectTypeScript(fullPath),
223
- detectBundler(fullPath, pkg),
483
+ detectTypeScript(fullPath, fsAdapter),
484
+ detectBundler(fullPath, pkg, fsAdapter),
224
485
  detectPackageManager(fullPath),
225
- detectSrcDir(fullPath),
226
- detectPublicDir(fullPath),
227
- detectGit(fullPath)
486
+ detectSrcDir(fullPath, fsAdapter),
487
+ detectPublicDir(fullPath, fsAdapter),
488
+ detectGit(fullPath, fsAdapter)
228
489
  ]);
229
- const lockfile = await detectLockfile(fullPath, packageManager);
230
- const nextjsRouter = frameworkInfo.framework === "nextjs" ? await detectNextjsRouter(fullPath, srcDir) : void 0;
490
+ const lockfile = await detectLockfile(fullPath, packageManager, fsAdapter);
491
+ const nextjsRouter = frameworkInfo.framework === "nextjs" ? await detectNextjsRouter(fullPath, srcDir, fsAdapter) : void 0;
492
+ const vueVersion = frameworkInfo.framework === "vue" ? "3" : void 0;
493
+ const vueApi = frameworkInfo.framework === "vue" ? await detectVueApi(fullPath, srcDir, fsAdapter) : void 0;
231
494
  const dependencies = pkg["dependencies"] || {};
232
495
  const devDependencies = pkg["devDependencies"] || {};
233
496
  const context = {
@@ -257,7 +520,12 @@ async function detectContext(projectRoot) {
257
520
  hasGit: gitInfo.hasGit,
258
521
  gitHooksPath: gitInfo.gitHooksPath,
259
522
  // Next.js specific
260
- nextjsRouter
523
+ nextjsRouter,
524
+ // Vue.js specific
525
+ vueVersion,
526
+ vueApi,
527
+ // Filesystem adapter (for testing with memfs)
528
+ fsAdapter
261
529
  };
262
530
  detectionCache.set(fullPath, context);
263
531
  logger.debug(`Context detected successfully for ${fullPath}`, {
@@ -268,184 +536,8 @@ async function detectContext(projectRoot) {
268
536
  return context;
269
537
  }
270
538
 
271
- // src/core/plugin-tracker.ts
272
- import { join as join2 } from "path";
273
- var CONFIG_FILE_NAME = ".configjsrc";
274
- var CONFIG_VERSION = "1.0.0";
275
- var PluginTracker = class {
276
- configPath;
277
- config = null;
278
- constructor(projectRoot) {
279
- this.configPath = join2(projectRoot, CONFIG_FILE_NAME);
280
- }
281
- /**
282
- * Charge la configuration depuis le fichier
283
- */
284
- async load() {
285
- try {
286
- const exists = await checkPathExists(this.configPath);
287
- if (!exists) {
288
- this.config = {
289
- version: CONFIG_VERSION,
290
- installedPlugins: [],
291
- lastUpdated: (/* @__PURE__ */ new Date()).toISOString()
292
- };
293
- return;
294
- }
295
- const content = await readFileContent(this.configPath);
296
- this.config = JSON.parse(content);
297
- if (!this.config.version) {
298
- logger.warn("Old config format detected, migrating...");
299
- this.config.version = CONFIG_VERSION;
300
- await this.save();
301
- }
302
- } catch (error) {
303
- logger.error("Failed to load plugin configuration", error);
304
- this.config = {
305
- version: CONFIG_VERSION,
306
- installedPlugins: [],
307
- lastUpdated: (/* @__PURE__ */ new Date()).toISOString()
308
- };
309
- }
310
- }
311
- /**
312
- * Sauvegarde la configuration dans le fichier
313
- */
314
- async save() {
315
- if (!this.config) {
316
- throw new Error("Configuration not loaded");
317
- }
318
- this.config.lastUpdated = (/* @__PURE__ */ new Date()).toISOString();
319
- try {
320
- const content = JSON.stringify(this.config, null, 2);
321
- await writeFileContent(this.configPath, content);
322
- } catch (error) {
323
- logger.error("Failed to save plugin configuration", error);
324
- throw error;
325
- }
326
- }
327
- /**
328
- * Vérifie si un plugin est déjà installé
329
- */
330
- isInstalled(pluginName) {
331
- if (!this.config) {
332
- return false;
333
- }
334
- return this.config.installedPlugins.some((p) => p.name === pluginName);
335
- }
336
- /**
337
- * Récupère les informations d'un plugin installé
338
- */
339
- getPlugin(pluginName) {
340
- if (!this.config) {
341
- return void 0;
342
- }
343
- return this.config.installedPlugins.find((p) => p.name === pluginName);
344
- }
345
- /**
346
- * Récupère tous les plugins installés
347
- */
348
- getInstalledPlugins() {
349
- return this.config?.installedPlugins || [];
350
- }
351
- /**
352
- * Récupère tous les plugins installés d'une catégorie donnée
353
- */
354
- getPluginsByCategory(category) {
355
- if (!this.config) {
356
- return [];
357
- }
358
- return this.config.installedPlugins.filter((p) => p.category === category);
359
- }
360
- /**
361
- * Ajoute un plugin à la liste des installés
362
- */
363
- async addPlugin(plugin) {
364
- if (!this.config) {
365
- throw new Error("Configuration not loaded");
366
- }
367
- if (this.isInstalled(plugin.name)) {
368
- logger.warn(`Plugin ${plugin.name} is already marked as installed`);
369
- return;
370
- }
371
- const installedPlugin = {
372
- ...plugin,
373
- installedAt: (/* @__PURE__ */ new Date()).toISOString()
374
- };
375
- this.config.installedPlugins.push(installedPlugin);
376
- await this.save();
377
- logger.debug(`Marked plugin ${plugin.name} as installed`);
378
- }
379
- /**
380
- * Supprime un plugin de la liste des installés
381
- */
382
- async removePlugin(pluginName) {
383
- if (!this.config) {
384
- throw new Error("Configuration not loaded");
385
- }
386
- const index = this.config.installedPlugins.findIndex(
387
- (p) => p.name === pluginName
388
- );
389
- if (index === -1) {
390
- logger.warn(`Plugin ${pluginName} not found in installed plugins`);
391
- return;
392
- }
393
- this.config.installedPlugins.splice(index, 1);
394
- await this.save();
395
- logger.debug(`Removed plugin ${pluginName} from installed plugins`);
396
- }
397
- /**
398
- * Met à jour les informations d'un plugin installé
399
- */
400
- async updatePlugin(pluginName, updates) {
401
- if (!this.config) {
402
- throw new Error("Configuration not loaded");
403
- }
404
- const plugin = this.config.installedPlugins.find(
405
- (p) => p.name === pluginName
406
- );
407
- if (!plugin) {
408
- throw new Error(`Plugin ${pluginName} not found`);
409
- }
410
- Object.assign(plugin, updates);
411
- await this.save();
412
- logger.debug(`Updated plugin ${pluginName}`);
413
- }
414
- /**
415
- * Efface toute la configuration
416
- */
417
- async reset() {
418
- this.config = {
419
- version: CONFIG_VERSION,
420
- installedPlugins: [],
421
- lastUpdated: (/* @__PURE__ */ new Date()).toISOString()
422
- };
423
- await this.save();
424
- logger.info("Reset plugin configuration");
425
- }
426
- /**
427
- * Vérifie les conflits potentiels avec un nouveau plugin
428
- *
429
- * @param category - Catégorie du plugin à installer
430
- * @param exclusiveCategories - Catégories qui ne peuvent avoir qu'un seul plugin
431
- * @returns Liste des plugins en conflit
432
- */
433
- checkCategoryConflicts(category, exclusiveCategories = ["routing", "state"]) {
434
- if (!exclusiveCategories.includes(category)) {
435
- return [];
436
- }
437
- return this.getPluginsByCategory(category);
438
- }
439
- /**
440
- * Exporte la configuration pour affichage ou debug
441
- */
442
- export() {
443
- return this.config;
444
- }
445
- };
446
-
447
539
  export {
540
+ PluginTracker,
448
541
  DetectionError,
449
- detectContext,
450
- PluginTracker
542
+ detectContext
451
543
  };