@configjs/cli 1.0.2 → 1.0.4

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,11 +1,14 @@
1
1
  import {
2
2
  CompatibilityValidator,
3
3
  compatibilityRules
4
- } from "./chunk-SCNFVSYD.js";
4
+ } from "./chunk-G5QBS6TL.js";
5
5
  import {
6
- logger,
7
6
  pluginRegistry
8
- } from "./chunk-7ZLAP62L.js";
7
+ } from "./chunk-GICPMTU2.js";
8
+ import {
9
+ logger
10
+ } from "./chunk-5S5KGCVY.js";
11
+ import "./chunk-QGM4M3NI.js";
9
12
 
10
13
  // src/cli/commands/check.ts
11
14
  import { readFile } from "fs/promises";
@@ -0,0 +1,276 @@
1
+ // src/utils/logger.ts
2
+ import pc from "picocolors";
3
+ var Logger = class {
4
+ level = 1 /* INFO */;
5
+ setLevel(level) {
6
+ this.level = level;
7
+ }
8
+ debug(message, ...args) {
9
+ if (this.level <= 0 /* DEBUG */) {
10
+ console.log(pc.gray(`[DEBUG] ${message}`), ...args);
11
+ }
12
+ }
13
+ info(message, ...args) {
14
+ if (this.level <= 1 /* INFO */) {
15
+ console.log(pc.blue(`\u2139 ${message}`), ...args);
16
+ }
17
+ }
18
+ success(message, ...args) {
19
+ if (this.level <= 1 /* INFO */) {
20
+ console.log(pc.green(`\u2713 ${message}`), ...args);
21
+ }
22
+ }
23
+ warn(message, ...args) {
24
+ if (this.level <= 2 /* WARN */) {
25
+ console.warn(pc.yellow(`\u26A0 ${message}`), ...args);
26
+ }
27
+ }
28
+ error(message, ...args) {
29
+ if (this.level <= 3 /* ERROR */) {
30
+ console.error(pc.red(`\u2716 ${message}`), ...args);
31
+ }
32
+ }
33
+ step(message) {
34
+ if (this.level <= 1 /* INFO */) {
35
+ console.log(pc.cyan(`
36
+ \u2192 ${message}`));
37
+ }
38
+ }
39
+ box(title, content) {
40
+ if (this.level <= 1 /* INFO */) {
41
+ const maxLength = Math.max(
42
+ title.length,
43
+ ...content.map((line) => line.length)
44
+ );
45
+ const border = "\u2500".repeat(maxLength + 4);
46
+ console.log(pc.cyan(`\u250C${border}\u2510`));
47
+ console.log(pc.cyan(`\u2502 ${title.padEnd(maxLength)} \u2502`));
48
+ console.log(pc.cyan(`\u251C${border}\u2524`));
49
+ content.forEach((line) => {
50
+ console.log(pc.cyan(`\u2502 ${line.padEnd(maxLength)} \u2502`));
51
+ });
52
+ console.log(pc.cyan(`\u2514${border}\u2518`));
53
+ }
54
+ }
55
+ };
56
+ var logger = new Logger();
57
+
58
+ // src/utils/fs-helpers.ts
59
+ import fs from "fs-extra";
60
+ import { resolve, dirname, extname } from "path";
61
+ function normalizePath(path) {
62
+ return path.replace(/\\/g, "/");
63
+ }
64
+ async function readPackageJson(root) {
65
+ const packageJsonPath = resolve(root, "package.json");
66
+ if (!await fs.pathExists(packageJsonPath)) {
67
+ throw new Error(`package.json not found at ${packageJsonPath}`);
68
+ }
69
+ try {
70
+ const pkg = await fs.readJson(packageJsonPath);
71
+ logger.debug(`Read package.json from ${packageJsonPath}`);
72
+ return pkg;
73
+ } catch (error) {
74
+ const errorMessage = error instanceof Error ? error.message : String(error);
75
+ throw new Error(
76
+ `Failed to read package.json: ${errorMessage}. File may be invalid JSON.`
77
+ );
78
+ }
79
+ }
80
+ async function writePackageJson(root, pkg) {
81
+ const packageJsonPath = resolve(root, "package.json");
82
+ try {
83
+ await fs.writeJson(packageJsonPath, pkg, {
84
+ spaces: 2,
85
+ EOL: "\n"
86
+ });
87
+ logger.debug(`Wrote package.json to ${packageJsonPath}`);
88
+ } catch (error) {
89
+ const errorMessage = error instanceof Error ? error.message : String(error);
90
+ throw new Error(`Failed to write package.json: ${errorMessage}`);
91
+ }
92
+ }
93
+ async function readTsConfig(root) {
94
+ const possiblePaths = [
95
+ resolve(root, "tsconfig.json"),
96
+ resolve(root, "tsconfig.app.json"),
97
+ resolve(root, "tsconfig.node.json")
98
+ ];
99
+ for (const tsconfigPath of possiblePaths) {
100
+ if (await fs.pathExists(tsconfigPath)) {
101
+ try {
102
+ const config = await fs.readJson(tsconfigPath);
103
+ logger.debug(`Read tsconfig.json from ${tsconfigPath}`);
104
+ return config;
105
+ } catch (error) {
106
+ const errorMessage = error instanceof Error ? error.message : String(error);
107
+ logger.warn(
108
+ `Failed to parse tsconfig.json at ${tsconfigPath}: ${errorMessage}`
109
+ );
110
+ return null;
111
+ }
112
+ }
113
+ }
114
+ logger.debug("No tsconfig.json found");
115
+ return null;
116
+ }
117
+ async function checkPathExists(path) {
118
+ const fullPath = resolve(path);
119
+ return fs.pathExists(fullPath);
120
+ }
121
+ async function ensureDirectory(path) {
122
+ const fullPath = resolve(path);
123
+ try {
124
+ await fs.ensureDir(fullPath);
125
+ logger.debug(`Ensured directory exists: ${fullPath}`);
126
+ } catch (error) {
127
+ const errorMessage = error instanceof Error ? error.message : String(error);
128
+ throw new Error(`Failed to create directory ${fullPath}: ${errorMessage}`);
129
+ }
130
+ }
131
+ async function readFileContent(filePath, encoding = "utf-8") {
132
+ const fullPath = resolve(filePath);
133
+ if (!await fs.pathExists(fullPath)) {
134
+ throw new Error(`File not found: ${fullPath}`);
135
+ }
136
+ try {
137
+ const content = await fs.readFile(fullPath, encoding);
138
+ logger.debug(`Read file: ${fullPath}`);
139
+ return content;
140
+ } catch (error) {
141
+ const errorMessage = error instanceof Error ? error.message : String(error);
142
+ throw new Error(`Failed to read file ${fullPath}: ${errorMessage}`);
143
+ }
144
+ }
145
+ async function writeFileContent(filePath, content, encoding = "utf-8") {
146
+ const fullPath = resolve(filePath);
147
+ const parentDir = dirname(fullPath);
148
+ await ensureDirectory(parentDir);
149
+ try {
150
+ await fs.writeFile(fullPath, content, encoding);
151
+ logger.debug(`Wrote file: ${fullPath}`);
152
+ } catch (error) {
153
+ const errorMessage = error instanceof Error ? error.message : String(error);
154
+ throw new Error(`Failed to write file ${fullPath}: ${errorMessage}`);
155
+ }
156
+ }
157
+
158
+ // src/utils/package-manager.ts
159
+ import { execa } from "execa";
160
+ import fs2 from "fs-extra";
161
+ import { resolve as resolve2, join } from "path";
162
+ async function detectPackageManager(projectRoot) {
163
+ const root = resolve2(projectRoot);
164
+ const lockfiles = [
165
+ { file: "pnpm-lock.yaml", manager: "pnpm" },
166
+ { file: "yarn.lock", manager: "yarn" },
167
+ { file: "package-lock.json", manager: "npm" },
168
+ { file: "bun.lockb", manager: "bun" }
169
+ ];
170
+ for (const { file, manager } of lockfiles) {
171
+ const lockfilePath = join(root, file);
172
+ if (await fs2.pathExists(lockfilePath)) {
173
+ logger.debug(`Detected package manager: ${manager} (found ${file})`);
174
+ return manager;
175
+ }
176
+ }
177
+ logger.debug("No lockfile found, defaulting to npm");
178
+ return "npm";
179
+ }
180
+ async function installPackages(packages, options) {
181
+ if (packages.length === 0) {
182
+ logger.warn("No packages to install");
183
+ return { success: true, packages: [] };
184
+ }
185
+ const {
186
+ packageManager,
187
+ projectRoot,
188
+ dev = false,
189
+ exact = false,
190
+ silent = false
191
+ } = options;
192
+ logger.info(
193
+ `Installing ${packages.length} package(s) with ${packageManager}...`
194
+ );
195
+ try {
196
+ const command = getInstallCommand(packageManager, packages, { dev, exact });
197
+ const cwd = resolve2(projectRoot);
198
+ logger.debug(`Executing: ${command.join(" ")} in ${cwd}`);
199
+ const [cmd, ...args] = command;
200
+ if (!cmd) {
201
+ throw new Error("Command is empty");
202
+ }
203
+ const result = await execa(cmd, args, {
204
+ cwd,
205
+ stdio: silent ? "pipe" : "inherit",
206
+ env: {
207
+ ...process.env,
208
+ // Désactiver les prompts interactifs
209
+ npm_config_yes: "true",
210
+ YARN_ENABLE_IMMUTABLE_INSTALLS: "false"
211
+ }
212
+ });
213
+ if (result.exitCode !== 0) {
214
+ throw new Error(`Installation failed with exit code ${result.exitCode}`);
215
+ }
216
+ logger.success(`Successfully installed ${packages.length} package(s)`);
217
+ return {
218
+ success: true,
219
+ packages
220
+ };
221
+ } catch (error) {
222
+ const errorMessage = error instanceof Error ? error.message : String(error);
223
+ logger.error(`Failed to install packages: ${errorMessage}`);
224
+ return {
225
+ success: false,
226
+ packages,
227
+ error: errorMessage
228
+ };
229
+ }
230
+ }
231
+ function getInstallCommand(packageManager, packages, options) {
232
+ const { dev, exact } = options;
233
+ switch (packageManager) {
234
+ case "pnpm":
235
+ return [
236
+ "pnpm",
237
+ "add",
238
+ ...dev ? ["-D"] : [],
239
+ ...exact ? ["--save-exact"] : [],
240
+ ...packages
241
+ ];
242
+ case "yarn":
243
+ return [
244
+ "yarn",
245
+ "add",
246
+ ...dev ? ["--dev"] : [],
247
+ ...exact ? ["--exact"] : [],
248
+ ...packages
249
+ ];
250
+ case "bun":
251
+ return ["bun", "add", ...dev ? ["--dev"] : [], ...packages];
252
+ case "npm":
253
+ default:
254
+ return [
255
+ "npm",
256
+ "install",
257
+ ...dev ? ["--save-dev"] : [],
258
+ ...exact ? ["--save-exact"] : [],
259
+ ...packages
260
+ ];
261
+ }
262
+ }
263
+
264
+ export {
265
+ logger,
266
+ normalizePath,
267
+ readPackageJson,
268
+ writePackageJson,
269
+ readTsConfig,
270
+ checkPathExists,
271
+ ensureDirectory,
272
+ readFileContent,
273
+ writeFileContent,
274
+ detectPackageManager,
275
+ installPackages
276
+ };
@@ -0,0 +1,417 @@
1
+ import {
2
+ checkPathExists,
3
+ detectPackageManager,
4
+ logger,
5
+ readFileContent,
6
+ readPackageJson,
7
+ readTsConfig,
8
+ writeFileContent
9
+ } from "./chunk-5S5KGCVY.js";
10
+
11
+ // src/core/detector.ts
12
+ import { resolve, join } from "path";
13
+ import { platform, version } from "process";
14
+ var detectionCache = /* @__PURE__ */ new Map();
15
+ var DetectionError = class extends Error {
16
+ constructor(message, context) {
17
+ super(message);
18
+ this.context = context;
19
+ this.name = "DetectionError";
20
+ }
21
+ };
22
+ function detectFramework(pkg) {
23
+ const deps = {
24
+ ...pkg["dependencies"] || {},
25
+ ...pkg["devDependencies"] || {}
26
+ };
27
+ if (deps["react"]) {
28
+ return {
29
+ framework: "react",
30
+ version: deps["react"].replace(/[\^~]/, "")
31
+ };
32
+ }
33
+ if (deps["vue"]) {
34
+ return {
35
+ framework: "vue",
36
+ version: deps["vue"].replace(/[\^~]/, "")
37
+ };
38
+ }
39
+ if (deps["svelte"]) {
40
+ return {
41
+ framework: "svelte",
42
+ version: deps["svelte"].replace(/[\^~]/, "")
43
+ };
44
+ }
45
+ throw new DetectionError(
46
+ "No supported framework detected. Supported frameworks: React, Vue, Svelte",
47
+ { dependencies: Object.keys(deps) }
48
+ );
49
+ }
50
+ async function detectBundler(projectRoot, pkg) {
51
+ const deps = {
52
+ ...pkg["dependencies"] || {},
53
+ ...pkg["devDependencies"] || {}
54
+ };
55
+ 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"));
57
+ if (viteConfigExists) {
58
+ return {
59
+ bundler: "vite",
60
+ version: deps["vite"].replace(/[\^~]/, "")
61
+ };
62
+ }
63
+ }
64
+ if (deps["react-scripts"]) {
65
+ return {
66
+ bundler: "cra",
67
+ version: deps["react-scripts"].replace(/[\^~]/, "")
68
+ };
69
+ }
70
+ if (deps["webpack"]) {
71
+ const webpackConfigExists = await checkPathExists(join(projectRoot, "webpack.config.js")) || await checkPathExists(join(projectRoot, "webpack.config.ts"));
72
+ if (webpackConfigExists) {
73
+ return {
74
+ bundler: "webpack",
75
+ version: deps["webpack"].replace(/[\^~]/, "")
76
+ };
77
+ }
78
+ }
79
+ if (deps["@rspack/core"]) {
80
+ return {
81
+ bundler: "rspack",
82
+ version: deps["@rspack/core"].replace(/[\^~]/, "")
83
+ };
84
+ }
85
+ return {
86
+ bundler: null,
87
+ version: null
88
+ };
89
+ }
90
+ async function detectTypeScript(projectRoot) {
91
+ const tsConfig = await readTsConfig(projectRoot);
92
+ if (tsConfig) {
93
+ const possiblePaths = [
94
+ join(projectRoot, "tsconfig.json"),
95
+ join(projectRoot, "tsconfig.app.json"),
96
+ join(projectRoot, "tsconfig.node.json")
97
+ ];
98
+ for (const path of possiblePaths) {
99
+ if (await checkPathExists(path)) {
100
+ return {
101
+ typescript: true,
102
+ tsconfigPath: path
103
+ };
104
+ }
105
+ }
106
+ return {
107
+ typescript: true,
108
+ tsconfigPath: join(projectRoot, "tsconfig.json")
109
+ };
110
+ }
111
+ return {
112
+ typescript: false
113
+ };
114
+ }
115
+ async function detectSrcDir(projectRoot) {
116
+ const possibleDirs = ["src", "app", "source", "lib"];
117
+ for (const dir of possibleDirs) {
118
+ const dirPath = join(projectRoot, dir);
119
+ if (await checkPathExists(dirPath)) {
120
+ return dir;
121
+ }
122
+ }
123
+ return "src";
124
+ }
125
+ async function detectPublicDir(projectRoot) {
126
+ const possibleDirs = ["public", "static", "assets"];
127
+ for (const dir of possibleDirs) {
128
+ const dirPath = join(projectRoot, dir);
129
+ if (await checkPathExists(dirPath)) {
130
+ return dir;
131
+ }
132
+ }
133
+ return "public";
134
+ }
135
+ async function detectGit(projectRoot) {
136
+ const gitDir = join(projectRoot, ".git");
137
+ const hasGit = await checkPathExists(gitDir);
138
+ if (!hasGit) {
139
+ return { hasGit: false };
140
+ }
141
+ const hooksPath = join(gitDir, "hooks");
142
+ const hasHooks = await checkPathExists(hooksPath);
143
+ return {
144
+ hasGit: true,
145
+ gitHooksPath: hasHooks ? hooksPath : void 0
146
+ };
147
+ }
148
+ async function detectLockfile(projectRoot, packageManager) {
149
+ const lockfiles = {
150
+ npm: "package-lock.json",
151
+ yarn: "yarn.lock",
152
+ pnpm: "pnpm-lock.yaml",
153
+ bun: "bun.lockb"
154
+ };
155
+ const lockfile = lockfiles[packageManager];
156
+ const lockfilePath = join(projectRoot, lockfile);
157
+ if (await checkPathExists(lockfilePath)) {
158
+ return lockfile;
159
+ }
160
+ return lockfile;
161
+ }
162
+ async function detectContext(projectRoot) {
163
+ const fullPath = resolve(projectRoot);
164
+ if (detectionCache.has(fullPath)) {
165
+ logger.debug(`Using cached context for ${fullPath}`);
166
+ const cached = detectionCache.get(fullPath);
167
+ if (cached) {
168
+ return cached;
169
+ }
170
+ }
171
+ logger.debug(`Detecting context for project: ${fullPath}`);
172
+ let pkg;
173
+ try {
174
+ pkg = await readPackageJson(fullPath);
175
+ } catch (error) {
176
+ const errorMessage = error instanceof Error ? error.message : String(error);
177
+ throw new DetectionError(
178
+ `Invalid project: package.json not found or invalid. ${errorMessage}`,
179
+ { projectRoot: fullPath }
180
+ );
181
+ }
182
+ const [
183
+ frameworkInfo,
184
+ typescriptInfo,
185
+ bundlerInfo,
186
+ packageManager,
187
+ srcDir,
188
+ publicDir,
189
+ gitInfo
190
+ ] = await Promise.all([
191
+ Promise.resolve(detectFramework(pkg)),
192
+ detectTypeScript(fullPath),
193
+ detectBundler(fullPath, pkg),
194
+ detectPackageManager(fullPath),
195
+ detectSrcDir(fullPath),
196
+ detectPublicDir(fullPath),
197
+ detectGit(fullPath)
198
+ ]);
199
+ const lockfile = await detectLockfile(fullPath, packageManager);
200
+ const dependencies = pkg["dependencies"] || {};
201
+ const devDependencies = pkg["devDependencies"] || {};
202
+ const context = {
203
+ // Framework
204
+ framework: frameworkInfo.framework,
205
+ frameworkVersion: frameworkInfo.version,
206
+ // Bundler
207
+ bundler: bundlerInfo.bundler,
208
+ bundlerVersion: bundlerInfo.version,
209
+ // Language
210
+ typescript: typescriptInfo.typescript,
211
+ tsconfigPath: typescriptInfo.tsconfigPath,
212
+ // Package Manager
213
+ packageManager,
214
+ lockfile,
215
+ // Structure
216
+ projectRoot: fullPath,
217
+ srcDir,
218
+ publicDir,
219
+ // Environment
220
+ os: platform,
221
+ nodeVersion: version,
222
+ // Dependencies
223
+ dependencies,
224
+ devDependencies,
225
+ // Git
226
+ hasGit: gitInfo.hasGit,
227
+ gitHooksPath: gitInfo.gitHooksPath
228
+ };
229
+ detectionCache.set(fullPath, context);
230
+ logger.debug(`Context detected successfully for ${fullPath}`, {
231
+ framework: context.framework,
232
+ bundler: context.bundler,
233
+ typescript: context.typescript
234
+ });
235
+ return context;
236
+ }
237
+
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
+ export {
415
+ detectContext,
416
+ PluginTracker
417
+ };