slicejs-cli 2.8.5 → 2.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,384 +1,437 @@
1
- // commands/utils/updateManager.js
2
-
3
- import { exec } from "child_process";
4
- import { promisify } from "util";
5
- import inquirer from "inquirer";
6
- import ora from "ora";
7
- import Print from "../Print.js";
8
- import versionChecker from "./VersionChecker.js";
9
- import { getProjectRoot } from "../utils/PathHelper.js";
10
- import path from "path";
11
- import { fileURLToPath } from "url";
12
-
13
- const execAsync = promisify(exec);
14
-
15
- class UpdateManager {
16
- constructor() {
17
- this.packagesToUpdate = [];
18
- }
19
-
20
- async detectCliInstall() {
21
- try {
22
- const moduleDir = path.dirname(fileURLToPath(import.meta.url));
23
- const cliRoot = path.join(moduleDir, '../../');
24
- const projectRoot = getProjectRoot(import.meta.url);
25
- let globalPrefix = '';
26
- try {
27
- const { stdout } = await execAsync('npm config get prefix');
28
- globalPrefix = stdout.toString().trim();
29
- } catch {}
30
- const localNodeModules = path.join(projectRoot, 'node_modules');
31
- const globalNodeModules = globalPrefix ? path.join(globalPrefix, 'node_modules') : '';
32
-
33
- if (cliRoot.startsWith(localNodeModules)) {
34
- return { type: 'local', cliRoot, projectRoot, globalPrefix };
35
- }
36
- if (globalNodeModules && cliRoot.startsWith(globalNodeModules)) {
37
- return { type: 'global', cliRoot, projectRoot, globalPrefix };
38
- }
39
- return { type: 'unknown', cliRoot, projectRoot, globalPrefix };
40
- } catch (error) {
41
- return { type: 'unknown' };
42
- }
43
- }
44
-
45
- /**
46
- * Check for available updates and return structured info
47
- */
48
- async checkForUpdates() {
49
- try {
50
- const updateInfo = await versionChecker.checkForUpdates(true); // Silent mode
51
-
52
- if (!updateInfo) {
53
- return null;
54
- }
55
-
56
- const updates = [];
57
-
58
- if (updateInfo.cli.status === 'outdated') {
59
- updates.push({
60
- name: 'slicejs-cli',
61
- displayName: 'Slice.js CLI',
62
- current: updateInfo.cli.current,
63
- latest: updateInfo.cli.latest,
64
- type: 'cli'
65
- });
66
- }
67
-
68
- if (updateInfo.framework.status === 'outdated') {
69
- updates.push({
70
- name: 'slicejs-web-framework',
71
- displayName: 'Slice.js Framework',
72
- current: updateInfo.framework.current,
73
- latest: updateInfo.framework.latest,
74
- type: 'framework'
75
- });
76
- }
77
-
78
- return {
79
- hasUpdates: updates.length > 0,
80
- updates,
81
- allCurrent: updateInfo.cli.status === 'current' && updateInfo.framework.status === 'current'
82
- };
83
- } catch (error) {
84
- Print.error(`Checking for updates: ${error.message}`);
85
- return null;
86
- }
87
- }
88
-
89
- /**
90
- * Display available updates in a formatted way
91
- */
92
- displayUpdates(updateInfo) {
93
- if (!updateInfo || !updateInfo.hasUpdates) {
94
- return;
95
- }
96
-
97
- console.log('');
98
- Print.warning('📦 Available Updates:');
99
- console.log('');
100
-
101
- updateInfo.updates.forEach(pkg => {
102
- console.log(` ${pkg.type === 'cli' ? '🔧' : '⚡'} ${pkg.displayName}`);
103
- console.log(` ${pkg.current} ${pkg.latest}`);
104
- });
105
-
106
- console.log('');
107
- console.log(' 📚 Changelog: https://github.com/VKneider/slice.js/releases');
108
- console.log('');
109
- }
110
-
111
- /**
112
- * Prompt user to select which packages to update
113
- */
114
- async promptForUpdates(updateInfo, options = {}) {
115
- if (!updateInfo || !updateInfo.hasUpdates) {
116
- return [];
117
- }
118
-
119
- // If --yes flag is set, return all updates
120
- if (options.yes) {
121
- return updateInfo.updates.map(pkg => pkg.name);
122
- }
123
-
124
- // If specific package flags are set
125
- if (options.cli || options.framework) {
126
- const selected = [];
127
- if (options.cli) {
128
- const cliUpdate = updateInfo.updates.find(pkg => pkg.type === 'cli');
129
- if (cliUpdate) selected.push(cliUpdate.name);
130
- }
131
- if (options.framework) {
132
- const frameworkUpdate = updateInfo.updates.find(pkg => pkg.type === 'framework');
133
- if (frameworkUpdate) selected.push(frameworkUpdate.name);
134
- }
135
- return selected;
136
- }
137
-
138
- // Interactive selection
139
- const choices = updateInfo.updates.map(pkg => ({
140
- name: `${pkg.displayName} (${pkg.current} ${pkg.latest})`,
141
- value: pkg.name,
142
- checked: true
143
- }));
144
-
145
- const answers = await inquirer.prompt([
146
- {
147
- type: 'checkbox',
148
- name: 'packages',
149
- message: 'Which packages do you want to update?',
150
- choices,
151
- validate: (answer) => {
152
- if (answer.length === 0) {
153
- return 'You must select at least one package';
154
- }
155
- return true;
156
- }
157
- }
158
- ]);
159
-
160
- return answers.packages;
161
- }
162
-
163
- async buildUpdatePlan(packages) {
164
- const plan = [];
165
- const info = await this.detectCliInstall();
166
- for (const pkg of packages) {
167
- if (pkg === 'slicejs-cli') {
168
- if (info.type === 'global') {
169
- plan.push({ package: pkg, target: 'global', command: 'npm install -g slicejs-cli@latest' });
170
- } else {
171
- plan.push({ package: pkg, target: 'project', command: 'npm install slicejs-cli@latest' });
172
- }
173
- } else if (pkg === 'slicejs-web-framework') {
174
- plan.push({ package: pkg, target: 'project', command: 'npm install slicejs-web-framework@latest' });
175
- } else {
176
- plan.push({ package: pkg, target: 'project', command: `npm install ${pkg}@latest` });
177
- }
178
- }
179
- return plan;
180
- }
181
-
182
- /**
183
- * Execute npm update command for a specific package
184
- */
185
- async updatePackage(packageName) {
186
- try {
187
- let installCmd = `npm install ${packageName}@latest`;
188
- let uninstallCmd = `npm uninstall ${packageName}`;
189
- let options = {};
190
-
191
- if (packageName === 'slicejs-cli') {
192
- const info = await this.detectCliInstall();
193
- if (info.type === 'global') {
194
- installCmd = `npm install -g slicejs-cli@latest`;
195
- uninstallCmd = `npm uninstall -g slicejs-cli`;
196
- } else {
197
- options.cwd = info.projectRoot || getProjectRoot(import.meta.url);
198
- }
199
- } else {
200
- options.cwd = getProjectRoot(import.meta.url);
201
- }
202
-
203
- // Try uninstall first (ignore failure)
204
- try {
205
- await execAsync(uninstallCmd, options);
206
- } catch {}
207
-
208
- const { stdout, stderr } = await execAsync(installCmd, options);
209
-
210
- return {
211
- success: true,
212
- packageName,
213
- stdout,
214
- stderr
215
- };
216
- } catch (error) {
217
- return {
218
- success: false,
219
- packageName,
220
- error: error.message
221
- };
222
- }
223
- }
224
-
225
- /**
226
- * Update multiple packages with progress indication
227
- */
228
- async installUpdates(packages) {
229
- const results = [];
230
-
231
- for (const packageName of packages) {
232
- const spinner = ora(`Updating ${packageName}...`).start();
233
-
234
- try {
235
- const result = await this.updatePackage(packageName);
236
-
237
- if (result.success) {
238
- spinner.succeed(`${packageName} updated successfully`);
239
- results.push({ packageName, success: true });
240
- } else {
241
- spinner.fail(`Error updating ${packageName}`);
242
- Print.error(`Details: ${result.error}`);
243
- results.push({ packageName, success: false, error: result.error });
244
- }
245
- } catch (error) {
246
- spinner.fail(`Error updating ${packageName}`);
247
- Print.error(`Details: ${error.message}`);
248
- results.push({ packageName, success: false, error: error.message });
249
- }
250
- }
251
-
252
- return results;
253
- }
254
-
255
- /**
256
- * Main method to check and prompt for updates
257
- */
258
- async checkAndPromptUpdates(options = {}) {
259
- const spinner = ora('Checking for updates...').start();
260
-
261
- try {
262
- const updateInfo = await this.checkForUpdates();
263
- spinner.stop();
264
-
265
- if (!updateInfo) {
266
- Print.error('Could not check for updates. Verify your internet connection.');
267
- return false;
268
- }
269
-
270
- if (updateInfo.allCurrent) {
271
- Print.success('✅ All components are up to date!');
272
- return true;
273
- }
274
-
275
- if (!updateInfo.hasUpdates) {
276
- Print.success('✅ All components are up to date!');
277
- return true;
278
- }
279
-
280
- // Display available updates
281
- this.displayUpdates(updateInfo);
282
-
283
- // Get packages to update
284
- const packagesToUpdate = await this.promptForUpdates(updateInfo, options);
285
-
286
- if (!packagesToUpdate || packagesToUpdate.length === 0) {
287
- Print.info('No packages selected for update.');
288
- return false;
289
- }
290
-
291
- // Show plan and confirm installation if not auto-confirmed
292
- let plan = await this.buildUpdatePlan(packagesToUpdate);
293
- console.log('');
294
- Print.info('🧭 Update plan:');
295
- plan.forEach(item => {
296
- const where = item.target === 'global' ? 'GLOBAL' : 'PROJECT';
297
- console.log(` • ${item.package} ${where}`);
298
- console.log(` ${item.command}`);
299
- });
300
- console.log('');
301
-
302
- const cliInfo = await this.detectCliInstall();
303
- if (cliInfo.type === 'global' && !packagesToUpdate.includes('slicejs-cli')) {
304
- if (!options.yes && !options.cli) {
305
- const { addCli } = await inquirer.prompt([
306
- {
307
- type: 'confirm',
308
- name: 'addCli',
309
- message: 'Global CLI detected. Add the global CLI update to the plan?',
310
- default: true
311
- }
312
- ]);
313
- if (addCli) {
314
- packagesToUpdate.push('slicejs-cli');
315
- plan = await this.buildUpdatePlan(packagesToUpdate);
316
- console.log('');
317
- Print.info('🧭 Updated plan:');
318
- plan.forEach(item => {
319
- const where = item.target === 'global' ? 'GLOBAL' : 'PROJECT';
320
- console.log(` • ${item.package} ${where}`);
321
- console.log(` ${item.command}`);
322
- });
323
- console.log('');
324
- }
325
- } else {
326
- Print.warning('Global CLI detected. It is recommended to update slicejs-cli globally to keep aligned with the framework.');
327
- console.log(' Suggestion: npm install -g slicejs-cli@latest');
328
- console.log('');
329
- }
330
- }
331
-
332
- if (!options.yes && !options.cli && !options.framework) {
333
- const { confirm } = await inquirer.prompt([
334
- {
335
- type: 'confirm',
336
- name: 'confirm',
337
- message: 'Do you want to proceed with the update according to the plan shown?',
338
- default: true
339
- }
340
- ]);
341
-
342
- if (!confirm) {
343
- Print.info('Update cancelled.');
344
- return false;
345
- }
346
- }
347
-
348
- console.log(''); // Line break
349
- Print.info('📥 Installing updates...');
350
- console.log('');
351
-
352
- // Install updates
353
- const results = await this.installUpdates(packagesToUpdate);
354
-
355
- // Summary
356
- console.log('');
357
- const successCount = results.filter(r => r.success).length;
358
- const failCount = results.filter(r => !r.success).length;
359
-
360
- if (failCount === 0) {
361
- Print.success(`✅ ${successCount} package(s) updated successfully!`);
362
- } else {
363
- Print.warning(`⚠️ ${successCount} successful, ${failCount} failed`);
364
- }
365
-
366
- if (successCount > 0) {
367
- console.log('');
368
- Print.info('💡 It is recommended to restart the development server if it is running.');
369
- }
370
-
371
- return failCount === 0;
372
-
373
- } catch (error) {
374
- spinner.stop();
375
- Print.error(`Error during update: ${error.message}`);
376
- return false;
377
- }
378
- }
379
- }
380
-
381
- // Singleton instance
382
- const updateManager = new UpdateManager();
383
-
384
- export default updateManager;
1
+ // commands/utils/updateManager.js
2
+
3
+ import { exec } from "child_process";
4
+ import { promisify } from "util";
5
+ import inquirer from "inquirer";
6
+ import ora from "ora";
7
+ import Print from "../Print.js";
8
+ import versionChecker from "./VersionChecker.js";
9
+ import { getProjectRoot } from "../utils/PathHelper.js";
10
+ import path from "path";
11
+ import { fileURLToPath } from "url";
12
+ import fs from "fs-extra";
13
+
14
+ const execAsync = promisify(exec);
15
+
16
+ class UpdateManager {
17
+ constructor() {
18
+ this.packagesToUpdate = [];
19
+ }
20
+
21
+ async detectCliInstall() {
22
+ try {
23
+ const moduleDir = path.dirname(fileURLToPath(import.meta.url));
24
+ const cliRoot = path.join(moduleDir, '../../');
25
+ const projectRoot = getProjectRoot(import.meta.url);
26
+ let globalPrefix = '';
27
+ try {
28
+ const { stdout } = await execAsync('npm config get prefix');
29
+ globalPrefix = stdout.toString().trim();
30
+ } catch {}
31
+ const localNodeModules = path.join(projectRoot, 'node_modules');
32
+ const globalNodeModules = globalPrefix ? path.join(globalPrefix, 'node_modules') : '';
33
+
34
+ if (cliRoot.startsWith(localNodeModules)) {
35
+ return { type: 'local', cliRoot, projectRoot, globalPrefix };
36
+ }
37
+ if (globalNodeModules && cliRoot.startsWith(globalNodeModules)) {
38
+ return { type: 'global', cliRoot, projectRoot, globalPrefix };
39
+ }
40
+ return { type: 'unknown', cliRoot, projectRoot, globalPrefix };
41
+ } catch (error) {
42
+ return { type: 'unknown' };
43
+ }
44
+ }
45
+
46
+ /**
47
+ * Check for available updates and return structured info
48
+ */
49
+ async checkForUpdates() {
50
+ try {
51
+ const updateInfo = await versionChecker.checkForUpdates(true); // Silent mode
52
+
53
+ if (!updateInfo) {
54
+ return null;
55
+ }
56
+
57
+ const updates = [];
58
+
59
+ if (updateInfo.cli.status === 'outdated') {
60
+ updates.push({
61
+ name: 'slicejs-cli',
62
+ displayName: 'Slice.js CLI',
63
+ current: updateInfo.cli.current,
64
+ latest: updateInfo.cli.latest,
65
+ type: 'cli'
66
+ });
67
+ }
68
+
69
+ if (updateInfo.framework.status === 'outdated') {
70
+ updates.push({
71
+ name: 'slicejs-web-framework',
72
+ displayName: 'Slice.js Framework',
73
+ current: updateInfo.framework.current,
74
+ latest: updateInfo.framework.latest,
75
+ type: 'framework'
76
+ });
77
+ }
78
+
79
+ return {
80
+ hasUpdates: updates.length > 0,
81
+ updates,
82
+ allCurrent: updateInfo.cli.status === 'current' && updateInfo.framework.status === 'current'
83
+ };
84
+ } catch (error) {
85
+ Print.error(`Checking for updates: ${error.message}`);
86
+ return null;
87
+ }
88
+ }
89
+
90
+ /**
91
+ * Display available updates in a formatted way
92
+ */
93
+ displayUpdates(updateInfo) {
94
+ if (!updateInfo || !updateInfo.hasUpdates) {
95
+ return;
96
+ }
97
+
98
+ console.log('');
99
+ Print.warning('📦 Available Updates:');
100
+ console.log('');
101
+
102
+ updateInfo.updates.forEach(pkg => {
103
+ console.log(` ${pkg.type === 'cli' ? '🔧' : '⚡'} ${pkg.displayName}`);
104
+ console.log(` ${pkg.current} → ${pkg.latest}`);
105
+ });
106
+
107
+ console.log('');
108
+ console.log(' 📚 Changelog: https://github.com/VKneider/slice.js/releases');
109
+ console.log('');
110
+ }
111
+
112
+ /**
113
+ * Prompt user to select which packages to update
114
+ */
115
+ async promptForUpdates(updateInfo, options = {}) {
116
+ if (!updateInfo || !updateInfo.hasUpdates) {
117
+ return [];
118
+ }
119
+
120
+ // If --yes flag is set, return all updates
121
+ if (options.yes) {
122
+ return updateInfo.updates.map(pkg => pkg.name);
123
+ }
124
+
125
+ // If specific package flags are set
126
+ if (options.cli || options.framework) {
127
+ const selected = [];
128
+ if (options.cli) {
129
+ const cliUpdate = updateInfo.updates.find(pkg => pkg.type === 'cli');
130
+ if (cliUpdate) selected.push(cliUpdate.name);
131
+ }
132
+ if (options.framework) {
133
+ const frameworkUpdate = updateInfo.updates.find(pkg => pkg.type === 'framework');
134
+ if (frameworkUpdate) selected.push(frameworkUpdate.name);
135
+ }
136
+ return selected;
137
+ }
138
+
139
+ // Interactive selection
140
+ const choices = updateInfo.updates.map(pkg => ({
141
+ name: `${pkg.displayName} (${pkg.current} → ${pkg.latest})`,
142
+ value: pkg.name,
143
+ checked: true
144
+ }));
145
+
146
+ const answers = await inquirer.prompt([
147
+ {
148
+ type: 'checkbox',
149
+ name: 'packages',
150
+ message: 'Which packages do you want to update?',
151
+ choices,
152
+ validate: (answer) => {
153
+ if (answer.length === 0) {
154
+ return 'You must select at least one package';
155
+ }
156
+ return true;
157
+ }
158
+ }
159
+ ]);
160
+
161
+ return answers.packages;
162
+ }
163
+
164
+ async buildUpdatePlan(packages) {
165
+ const plan = [];
166
+ const info = await this.detectCliInstall();
167
+ for (const pkg of packages) {
168
+ if (pkg === 'slicejs-cli') {
169
+ if (info.type === 'global') {
170
+ plan.push({ package: pkg, target: 'global', command: 'npm install -g slicejs-cli@latest' });
171
+ } else {
172
+ plan.push({ package: pkg, target: 'project', command: 'npm install slicejs-cli@latest' });
173
+ }
174
+ } else if (pkg === 'slicejs-web-framework') {
175
+ plan.push({ package: pkg, target: 'project', command: 'npm install slicejs-web-framework@latest' });
176
+ } else {
177
+ plan.push({ package: pkg, target: 'project', command: `npm install ${pkg}@latest` });
178
+ }
179
+ }
180
+ return plan;
181
+ }
182
+
183
+ /**
184
+ * Execute npm update command for a specific package
185
+ */
186
+ async updatePackage(packageName) {
187
+ try {
188
+ let installCmd = `npm install ${packageName}@latest`;
189
+ let uninstallCmd = `npm uninstall ${packageName}`;
190
+ let options = {};
191
+
192
+ if (packageName === 'slicejs-cli') {
193
+ const info = await this.detectCliInstall();
194
+ if (info.type === 'global') {
195
+ installCmd = `npm install -g slicejs-cli@latest`;
196
+ uninstallCmd = `npm uninstall -g slicejs-cli`;
197
+ } else {
198
+ options.cwd = info.projectRoot || getProjectRoot(import.meta.url);
199
+ }
200
+ } else {
201
+ options.cwd = getProjectRoot(import.meta.url);
202
+ }
203
+
204
+ // Try uninstall first (ignore failure)
205
+ try {
206
+ await execAsync(uninstallCmd, options);
207
+ } catch {}
208
+
209
+ const { stdout, stderr } = await execAsync(installCmd, options);
210
+
211
+ return {
212
+ success: true,
213
+ packageName,
214
+ stdout,
215
+ stderr
216
+ };
217
+ } catch (error) {
218
+ return {
219
+ success: false,
220
+ packageName,
221
+ error: error.message
222
+ };
223
+ }
224
+ }
225
+
226
+ /**
227
+ * Update multiple packages with progress indication
228
+ */
229
+ async installUpdates(packages) {
230
+ const results = [];
231
+
232
+ for (const packageName of packages) {
233
+ const spinner = ora(`Updating ${packageName}...`).start();
234
+
235
+ try {
236
+ const result = await this.updatePackage(packageName);
237
+
238
+ if (result.success) {
239
+ spinner.succeed(`${packageName} updated successfully`);
240
+ results.push({ packageName, success: true });
241
+ } else {
242
+ spinner.fail(`Error updating ${packageName}`);
243
+ Print.error(`Details: ${result.error}`);
244
+ results.push({ packageName, success: false, error: result.error });
245
+ }
246
+ } catch (error) {
247
+ spinner.fail(`Error updating ${packageName}`);
248
+ Print.error(`Details: ${error.message}`);
249
+ results.push({ packageName, success: false, error: error.message });
250
+ }
251
+ }
252
+
253
+ return results;
254
+ }
255
+
256
+ /**
257
+ * Main method to check and prompt for updates
258
+ */
259
+ async checkAndPromptUpdates(options = {}) {
260
+ const spinner = ora('Checking for updates...').start();
261
+
262
+ try {
263
+ const updateInfo = await this.checkForUpdates();
264
+ spinner.stop();
265
+
266
+ if (!updateInfo) {
267
+ Print.error('Could not check for updates. Verify your internet connection.');
268
+ return false;
269
+ }
270
+
271
+ if (updateInfo.allCurrent) {
272
+ Print.success('✅ All components are up to date!');
273
+ return true;
274
+ }
275
+
276
+ if (!updateInfo.hasUpdates) {
277
+ Print.success('✅ All components are up to date!');
278
+ return true;
279
+ }
280
+
281
+ // Display available updates
282
+ this.displayUpdates(updateInfo);
283
+
284
+ // Get packages to update
285
+ const packagesToUpdate = await this.promptForUpdates(updateInfo, options);
286
+
287
+ if (!packagesToUpdate || packagesToUpdate.length === 0) {
288
+ Print.info('No packages selected for update.');
289
+ return false;
290
+ }
291
+
292
+ // Show plan and confirm installation if not auto-confirmed
293
+ let plan = await this.buildUpdatePlan(packagesToUpdate);
294
+ console.log('');
295
+ Print.info('🧭 Update plan:');
296
+ plan.forEach(item => {
297
+ const where = item.target === 'global' ? 'GLOBAL' : 'PROJECT';
298
+ console.log(`${item.package} → ${where}`);
299
+ console.log(` ${item.command}`);
300
+ });
301
+ console.log('');
302
+
303
+ const cliInfo = await this.detectCliInstall();
304
+ if (cliInfo.type === 'global' && !packagesToUpdate.includes('slicejs-cli')) {
305
+ if (!options.yes && !options.cli) {
306
+ const { addCli } = await inquirer.prompt([
307
+ {
308
+ type: 'confirm',
309
+ name: 'addCli',
310
+ message: 'Global CLI detected. Add the global CLI update to the plan?',
311
+ default: true
312
+ }
313
+ ]);
314
+ if (addCli) {
315
+ packagesToUpdate.push('slicejs-cli');
316
+ plan = await this.buildUpdatePlan(packagesToUpdate);
317
+ console.log('');
318
+ Print.info('🧭 Updated plan:');
319
+ plan.forEach(item => {
320
+ const where = item.target === 'global' ? 'GLOBAL' : 'PROJECT';
321
+ console.log(`${item.package} → ${where}`);
322
+ console.log(` ${item.command}`);
323
+ });
324
+ console.log('');
325
+ }
326
+ } else {
327
+ Print.warning('Global CLI detected. It is recommended to update slicejs-cli globally to keep aligned with the framework.');
328
+ console.log(' Suggestion: npm install -g slicejs-cli@latest');
329
+ console.log('');
330
+ }
331
+ }
332
+
333
+ if (!options.yes && !options.cli && !options.framework) {
334
+ const { confirm } = await inquirer.prompt([
335
+ {
336
+ type: 'confirm',
337
+ name: 'confirm',
338
+ message: 'Do you want to proceed with the update according to the plan shown?',
339
+ default: true
340
+ }
341
+ ]);
342
+
343
+ if (!confirm) {
344
+ Print.info('Update cancelled.');
345
+ return false;
346
+ }
347
+ }
348
+
349
+ console.log(''); // Line break
350
+ Print.info('📥 Installing updates...');
351
+ console.log('');
352
+
353
+ // Install updates
354
+ const results = await this.installUpdates(packagesToUpdate);
355
+
356
+ // Summary
357
+ console.log('');
358
+ const successCount = results.filter(r => r.success).length;
359
+ const failCount = results.filter(r => !r.success).length;
360
+
361
+ if (failCount === 0) {
362
+ Print.success(`✅ ${successCount} package(s) updated successfully!`);
363
+ } else {
364
+ Print.warning(`⚠️ ${successCount} successful, ${failCount} failed`);
365
+ }
366
+
367
+ if (successCount > 0) {
368
+ console.log('');
369
+ Print.info('💡 It is recommended to restart the development server if it is running.');
370
+ }
371
+
372
+ const frameworkUpdated = results.find(r => r.packageName === 'slicejs-web-framework' && r.success);
373
+ if (frameworkUpdated) {
374
+ await this.updateApiIndexIfNeeded(options);
375
+ }
376
+
377
+ return failCount === 0;
378
+
379
+ } catch (error) {
380
+ spinner.stop();
381
+ Print.error(`Error during update: ${error.message}`);
382
+ return false;
383
+ }
384
+ }
385
+
386
+ async updateApiIndexIfNeeded(options = {}) {
387
+ try {
388
+ const projectRoot = getProjectRoot(import.meta.url);
389
+ const projectApiPath = path.join(projectRoot, 'api', 'index.js');
390
+ const frameworkApiPath = path.join(projectRoot, 'node_modules', 'slicejs-web-framework', 'api', 'index.js');
391
+
392
+ if (!await fs.pathExists(projectApiPath) || !await fs.pathExists(frameworkApiPath)) {
393
+ return;
394
+ }
395
+
396
+ const projectContent = await fs.readFile(projectApiPath, 'utf-8');
397
+ const frameworkContent = await fs.readFile(frameworkApiPath, 'utf-8');
398
+
399
+ if (projectContent === frameworkContent) {
400
+ return;
401
+ }
402
+
403
+ Print.warning('⚠️ Detected changes in framework api/index.js.');
404
+
405
+ let confirmUpdate = options.yes === true;
406
+ if (!confirmUpdate) {
407
+ const answers = await inquirer.prompt([
408
+ {
409
+ type: 'confirm',
410
+ name: 'confirm',
411
+ message: 'Update your project api/index.js to the latest framework version?',
412
+ default: false
413
+ }
414
+ ]);
415
+ confirmUpdate = answers.confirm;
416
+ }
417
+
418
+ if (!confirmUpdate) {
419
+ Print.info('Skipping api/index.js update.');
420
+ return;
421
+ }
422
+
423
+ const backupPath = `${projectApiPath}.bak`;
424
+ await fs.copy(projectApiPath, backupPath);
425
+ await fs.writeFile(projectApiPath, frameworkContent, 'utf-8');
426
+ Print.success('✅ api/index.js updated from framework template.');
427
+ Print.info(`Backup created at ${backupPath}`);
428
+ } catch (error) {
429
+ Print.error(`Failed to update api/index.js: ${error.message}`);
430
+ }
431
+ }
432
+ }
433
+
434
+ // Singleton instance
435
+ const updateManager = new UpdateManager();
436
+
437
+ export default updateManager;