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