@friggframework/devtools 2.0.0--canary.545.ae2019f.0 → 2.0.0--canary.549.a579cca.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.
Files changed (127) hide show
  1. package/frigg-cli/README.md +1 -1
  2. package/frigg-cli/__tests__/unit/commands/build.test.js +1 -1
  3. package/frigg-cli/__tests__/unit/commands/doctor.test.js +2 -0
  4. package/frigg-cli/__tests__/unit/commands/install.test.js +19 -23
  5. package/frigg-cli/__tests__/unit/dependencies.test.js +2 -2
  6. package/frigg-cli/build-command/index.js +11 -123
  7. package/frigg-cli/deploy-command/index.js +1 -83
  8. package/frigg-cli/doctor-command/index.js +16 -37
  9. package/frigg-cli/generate-iam-command.js +1 -21
  10. package/frigg-cli/index.js +6 -21
  11. package/frigg-cli/index.test.js +2 -7
  12. package/frigg-cli/init-command/backend-first-handler.js +42 -124
  13. package/frigg-cli/init-command/index.js +1 -2
  14. package/frigg-cli/init-command/template-handler.js +3 -13
  15. package/frigg-cli/install-command/backend-js.js +3 -3
  16. package/frigg-cli/install-command/environment-variables.js +19 -16
  17. package/frigg-cli/install-command/environment-variables.test.js +13 -12
  18. package/frigg-cli/install-command/index.js +9 -14
  19. package/frigg-cli/install-command/integration-file.js +3 -3
  20. package/frigg-cli/install-command/logger.js +12 -0
  21. package/frigg-cli/install-command/validate-package.js +9 -5
  22. package/frigg-cli/jest.config.js +1 -4
  23. package/frigg-cli/repair-command/index.js +128 -121
  24. package/frigg-cli/start-command/index.js +2 -324
  25. package/frigg-cli/ui-command/index.js +36 -58
  26. package/frigg-cli/utils/repo-detection.js +37 -85
  27. package/infrastructure/create-frigg-infrastructure.js +0 -93
  28. package/infrastructure/docs/iam-policy-templates.md +1 -1
  29. package/infrastructure/domains/networking/vpc-builder.test.js +4 -2
  30. package/infrastructure/domains/networking/vpc-resolver.test.js +1 -1
  31. package/infrastructure/domains/shared/resource-discovery.js +5 -5
  32. package/infrastructure/domains/shared/types/app-definition.js +0 -21
  33. package/infrastructure/domains/shared/types/discovery-result.test.js +1 -1
  34. package/infrastructure/domains/shared/utilities/base-definition-factory.js +1 -10
  35. package/infrastructure/domains/shared/utilities/base-definition-factory.test.js +2 -2
  36. package/infrastructure/infrastructure-composer.js +0 -2
  37. package/infrastructure/infrastructure-composer.test.js +2 -2
  38. package/management-ui/README.md +109 -245
  39. package/package.json +7 -8
  40. package/frigg-cli/__tests__/application/use-cases/AddApiModuleToIntegrationUseCase.test.js +0 -326
  41. package/frigg-cli/__tests__/application/use-cases/CreateApiModuleUseCase.test.js +0 -337
  42. package/frigg-cli/__tests__/domain/entities/ApiModule.test.js +0 -373
  43. package/frigg-cli/__tests__/domain/entities/AppDefinition.test.js +0 -313
  44. package/frigg-cli/__tests__/domain/services/IntegrationValidator.test.js +0 -269
  45. package/frigg-cli/__tests__/domain/value-objects/IntegrationName.test.js +0 -82
  46. package/frigg-cli/__tests__/infrastructure/adapters/IntegrationJsUpdater.test.js +0 -408
  47. package/frigg-cli/__tests__/infrastructure/repositories/FileSystemApiModuleRepository.test.js +0 -583
  48. package/frigg-cli/__tests__/infrastructure/repositories/FileSystemAppDefinitionRepository.test.js +0 -314
  49. package/frigg-cli/__tests__/infrastructure/repositories/FileSystemIntegrationRepository.test.js +0 -383
  50. package/frigg-cli/__tests__/unit/commands/init.test.js +0 -406
  51. package/frigg-cli/__tests__/unit/commands/provider-dispatch.test.js +0 -383
  52. package/frigg-cli/__tests__/unit/commands/repair.test.js +0 -275
  53. package/frigg-cli/__tests__/unit/start-command/application/RunPreflightChecksUseCase.test.js +0 -411
  54. package/frigg-cli/__tests__/unit/start-command/infrastructure/DatabaseAdapter.test.js +0 -405
  55. package/frigg-cli/__tests__/unit/start-command/infrastructure/DockerAdapter.test.js +0 -496
  56. package/frigg-cli/__tests__/unit/start-command/presentation/InteractivePromptAdapter.test.js +0 -474
  57. package/frigg-cli/__tests__/unit/utils/output.test.js +0 -196
  58. package/frigg-cli/application/use-cases/AddApiModuleToIntegrationUseCase.js +0 -93
  59. package/frigg-cli/application/use-cases/CreateApiModuleUseCase.js +0 -93
  60. package/frigg-cli/application/use-cases/CreateIntegrationUseCase.js +0 -103
  61. package/frigg-cli/container.js +0 -172
  62. package/frigg-cli/docs/OUTPUT_MIGRATION_GUIDE.md +0 -286
  63. package/frigg-cli/domain/entities/ApiModule.js +0 -272
  64. package/frigg-cli/domain/entities/AppDefinition.js +0 -227
  65. package/frigg-cli/domain/entities/Integration.js +0 -198
  66. package/frigg-cli/domain/exceptions/DomainException.js +0 -24
  67. package/frigg-cli/domain/ports/IApiModuleRepository.js +0 -53
  68. package/frigg-cli/domain/ports/IAppDefinitionRepository.js +0 -43
  69. package/frigg-cli/domain/ports/IIntegrationRepository.js +0 -61
  70. package/frigg-cli/domain/services/IntegrationValidator.js +0 -185
  71. package/frigg-cli/domain/value-objects/IntegrationId.js +0 -42
  72. package/frigg-cli/domain/value-objects/IntegrationName.js +0 -60
  73. package/frigg-cli/domain/value-objects/SemanticVersion.js +0 -70
  74. package/frigg-cli/infrastructure/UnitOfWork.js +0 -46
  75. package/frigg-cli/infrastructure/adapters/BackendJsUpdater.js +0 -197
  76. package/frigg-cli/infrastructure/adapters/FileSystemAdapter.js +0 -224
  77. package/frigg-cli/infrastructure/adapters/IntegrationJsUpdater.js +0 -249
  78. package/frigg-cli/infrastructure/adapters/SchemaValidator.js +0 -92
  79. package/frigg-cli/infrastructure/repositories/FileSystemApiModuleRepository.js +0 -373
  80. package/frigg-cli/infrastructure/repositories/FileSystemAppDefinitionRepository.js +0 -116
  81. package/frigg-cli/infrastructure/repositories/FileSystemIntegrationRepository.js +0 -277
  82. package/frigg-cli/package-lock.json +0 -16226
  83. package/frigg-cli/start-command/application/RunPreflightChecksUseCase.js +0 -376
  84. package/frigg-cli/start-command/infrastructure/DatabaseAdapter.js +0 -591
  85. package/frigg-cli/start-command/infrastructure/DockerAdapter.js +0 -306
  86. package/frigg-cli/start-command/presentation/InteractivePromptAdapter.js +0 -329
  87. package/frigg-cli/templates/backend/.env.example +0 -62
  88. package/frigg-cli/templates/backend/.eslintrc.json +0 -12
  89. package/frigg-cli/templates/backend/.prettierrc +0 -6
  90. package/frigg-cli/templates/backend/docker-compose.yml +0 -22
  91. package/frigg-cli/templates/backend/index.js +0 -96
  92. package/frigg-cli/templates/backend/infrastructure.js +0 -12
  93. package/frigg-cli/templates/backend/jest.config.js +0 -17
  94. package/frigg-cli/templates/backend/package.json +0 -50
  95. package/frigg-cli/templates/backend/src/api-modules/.gitkeep +0 -10
  96. package/frigg-cli/templates/backend/src/base/.gitkeep +0 -7
  97. package/frigg-cli/templates/backend/src/integrations/.gitkeep +0 -10
  98. package/frigg-cli/templates/backend/src/integrations/ExampleIntegration.js +0 -65
  99. package/frigg-cli/templates/backend/src/utils/.gitkeep +0 -7
  100. package/frigg-cli/templates/backend/test/setup.js +0 -30
  101. package/frigg-cli/templates/backend/ui-extensions/.gitkeep +0 -0
  102. package/frigg-cli/templates/backend/ui-extensions/README.md +0 -77
  103. package/frigg-cli/utils/__tests__/provider-helper.test.js +0 -55
  104. package/frigg-cli/utils/__tests__/repo-detection.test.js +0 -436
  105. package/frigg-cli/utils/output.js +0 -382
  106. package/frigg-cli/utils/provider-helper.js +0 -75
  107. package/frigg-cli/validate-command/__tests__/adapters/validate-command.test.js +0 -205
  108. package/frigg-cli/validate-command/__tests__/application/validate-app-use-case.test.js +0 -104
  109. package/frigg-cli/validate-command/__tests__/domain/fix-suggestion.test.js +0 -153
  110. package/frigg-cli/validate-command/__tests__/domain/validation-error.test.js +0 -162
  111. package/frigg-cli/validate-command/__tests__/domain/validation-result.test.js +0 -152
  112. package/frigg-cli/validate-command/__tests__/infrastructure/api-module-validator.test.js +0 -332
  113. package/frigg-cli/validate-command/__tests__/infrastructure/app-definition-validator.test.js +0 -191
  114. package/frigg-cli/validate-command/__tests__/infrastructure/integration-class-validator.test.js +0 -146
  115. package/frigg-cli/validate-command/__tests__/infrastructure/template-validation.test.js +0 -155
  116. package/frigg-cli/validate-command/adapters/cli/validate-command.js +0 -199
  117. package/frigg-cli/validate-command/application/use-cases/validate-app-use-case.js +0 -35
  118. package/frigg-cli/validate-command/domain/entities/validation-result.js +0 -74
  119. package/frigg-cli/validate-command/domain/value-objects/fix-suggestion.js +0 -74
  120. package/frigg-cli/validate-command/domain/value-objects/validation-error.js +0 -68
  121. package/frigg-cli/validate-command/infrastructure/validators/api-module-validator.js +0 -181
  122. package/frigg-cli/validate-command/infrastructure/validators/app-definition-validator.js +0 -128
  123. package/frigg-cli/validate-command/infrastructure/validators/integration-class-validator.js +0 -113
  124. package/infrastructure/domains/admin-scripts/admin-script-builder.js +0 -200
  125. package/infrastructure/domains/admin-scripts/admin-script-builder.test.js +0 -499
  126. package/infrastructure/domains/admin-scripts/index.js +0 -5
  127. package/infrastructure/jest.config.js +0 -16
@@ -1,382 +0,0 @@
1
- /**
2
- * Output - Unified CLI output and interaction utilities
3
- *
4
- * Provides consistent formatting, colors, and interactive prompts across all CLI commands.
5
- * Replaces direct usage of chalk, console.log, inquirer, and readline.
6
- *
7
- * @example
8
- * const output = require('./utils/output');
9
- *
10
- * output.success('Module installed successfully');
11
- * output.error('Failed to connect to database', error);
12
- * output.info('Starting deployment...');
13
- *
14
- * const spinner = output.spinner('Downloading dependencies...');
15
- * // do work
16
- * spinner.succeed('Dependencies downloaded');
17
- *
18
- * const answer = await output.prompt({
19
- * type: 'confirm',
20
- * name: 'continue',
21
- * message: 'Continue with installation?'
22
- * });
23
- */
24
-
25
- const chalk = require('chalk');
26
- const { select, input, confirm, checkbox, password } = require('@inquirer/prompts');
27
-
28
- class Output {
29
- /**
30
- * Display a success message with a checkmark
31
- * @param {string} message - Success message to display
32
- */
33
- success(message) {
34
- console.log(chalk.green('✓'), chalk.green(message));
35
- }
36
-
37
- /**
38
- * Display an error message with an X mark
39
- * @param {string} message - Error message to display
40
- * @param {Error} [error] - Optional error object to display
41
- */
42
- error(message, error) {
43
- console.error(chalk.red('✗'), chalk.red(message));
44
- if (error && process.env.DEBUG) {
45
- console.error(chalk.gray(error.stack || error.message));
46
- }
47
- }
48
-
49
- /**
50
- * Display an info message with an info icon
51
- * @param {string} message - Info message to display
52
- */
53
- info(message) {
54
- console.log(chalk.blue('ℹ'), message);
55
- }
56
-
57
- /**
58
- * Display a warning message
59
- * @param {string} message - Warning message to display
60
- */
61
- warn(message) {
62
- console.warn(chalk.yellow('⚠'), chalk.yellow(message));
63
- }
64
-
65
- /**
66
- * Display a debug message (only when DEBUG env var is set)
67
- * @param {string} message - Debug message to display
68
- */
69
- debug(message) {
70
- if (process.env.DEBUG) {
71
- console.log(chalk.gray('🐛'), chalk.gray(message));
72
- }
73
- }
74
-
75
- /**
76
- * Display a header/title
77
- * @param {string} title - Title to display
78
- */
79
- header(title) {
80
- console.log('');
81
- console.log(chalk.bold.cyan(title));
82
- console.log(chalk.cyan('─'.repeat(title.length)));
83
- }
84
-
85
- /**
86
- * Display a section separator
87
- */
88
- separator() {
89
- console.log(chalk.gray('─'.repeat(50)));
90
- }
91
-
92
- /**
93
- * Display a blank line
94
- */
95
- newline() {
96
- console.log('');
97
- }
98
-
99
- /**
100
- * Display a table of data
101
- * @param {Array<Object>} data - Array of objects to display
102
- * @param {Array<string>} [columns] - Column keys to display (defaults to all)
103
- */
104
- table(data, columns) {
105
- if (!data || data.length === 0) {
106
- this.info('No data to display');
107
- return;
108
- }
109
-
110
- const keys = columns || Object.keys(data[0]);
111
-
112
- // Calculate column widths
113
- const widths = {};
114
- keys.forEach(key => {
115
- widths[key] = Math.max(
116
- key.length,
117
- ...data.map(row => String(row[key] || '').length)
118
- );
119
- });
120
-
121
- // Print header
122
- const headerRow = keys.map(key =>
123
- chalk.bold(key.padEnd(widths[key]))
124
- ).join(' ');
125
- console.log(headerRow);
126
- console.log(keys.map(key =>
127
- '─'.repeat(widths[key])
128
- ).join(' '));
129
-
130
- // Print rows
131
- data.forEach(row => {
132
- const dataRow = keys.map(key =>
133
- String(row[key] || '').padEnd(widths[key])
134
- ).join(' ');
135
- console.log(dataRow);
136
- });
137
- }
138
-
139
- /**
140
- * Display key-value pairs
141
- * @param {Object} data - Object with key-value pairs
142
- */
143
- keyValue(data) {
144
- const maxKeyLength = Math.max(...Object.keys(data).map(k => k.length));
145
-
146
- Object.entries(data).forEach(([key, value]) => {
147
- const formattedKey = chalk.gray(`${key.padEnd(maxKeyLength)}:`);
148
- console.log(`${formattedKey} ${value}`);
149
- });
150
- }
151
-
152
- /**
153
- * Display JSON with syntax highlighting
154
- * @param {Object} data - Data to display as JSON
155
- * @param {number} [indent=2] - Indentation level
156
- */
157
- json(data, indent = 2) {
158
- const json = JSON.stringify(data, null, indent);
159
- // Basic syntax highlighting
160
- const highlighted = json
161
- .replace(/"([^"]+)":/g, chalk.blue('"$1"') + ':') // keys
162
- .replace(/: "([^"]+)"/g, ': ' + chalk.green('"$1"')) // string values
163
- .replace(/: (\d+)/g, ': ' + chalk.yellow('$1')) // numbers
164
- .replace(/: (true|false|null)/g, ': ' + chalk.magenta('$1')); // booleans/null
165
-
166
- console.log(highlighted);
167
- }
168
-
169
- /**
170
- * Create a spinner for long-running operations
171
- * @param {string} text - Spinner text
172
- * @returns {Object} Spinner object with update/succeed/fail/stop methods
173
- */
174
- spinner(text) {
175
- const frames = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
176
- let frameIndex = 0;
177
- let interval = null;
178
- let currentText = text;
179
-
180
- const render = () => {
181
- process.stdout.write(`\r${chalk.cyan(frames[frameIndex])} ${currentText}`);
182
- frameIndex = (frameIndex + 1) % frames.length;
183
- };
184
-
185
- const clear = () => {
186
- process.stdout.write('\r' + ' '.repeat(process.stdout.columns || 80) + '\r');
187
- };
188
-
189
- const start = () => {
190
- if (interval) return;
191
- interval = setInterval(render, 80);
192
- };
193
-
194
- const stop = () => {
195
- if (interval) {
196
- clearInterval(interval);
197
- interval = null;
198
- clear();
199
- }
200
- };
201
-
202
- // Auto-start
203
- start();
204
-
205
- return {
206
- update: (newText) => {
207
- currentText = newText;
208
- },
209
- succeed: (message) => {
210
- stop();
211
- this.success(message || currentText);
212
- },
213
- fail: (message) => {
214
- stop();
215
- this.error(message || currentText);
216
- },
217
- stop: () => {
218
- stop();
219
- }
220
- };
221
- }
222
-
223
- /**
224
- * Prompt user with a question (uses @inquirer/prompts)
225
- * @param {Object} question - Question configuration
226
- * @returns {Promise<any>} User's answer
227
- */
228
- async prompt(question) {
229
- const { type, name, message, choices, initial, validate } = question;
230
-
231
- try {
232
- switch (type) {
233
- case 'select':
234
- return await select({ message, choices, default: initial });
235
-
236
- case 'input':
237
- return await input({ message, default: initial, validate });
238
-
239
- case 'confirm':
240
- return await confirm({ message, default: initial });
241
-
242
- case 'checkbox':
243
- return await checkbox({ message, choices, validate });
244
-
245
- case 'password':
246
- return await password({ message, validate });
247
-
248
- default:
249
- throw new Error(`Unknown prompt type: ${type}`);
250
- }
251
- } catch (error) {
252
- // User cancelled (Ctrl+C)
253
- if (error.message === 'User force closed the prompt') {
254
- this.warn('Operation cancelled by user');
255
- process.exit(0);
256
- }
257
- throw error;
258
- }
259
- }
260
-
261
- /**
262
- * Prompt for confirmation
263
- * @param {string} message - Confirmation message
264
- * @param {boolean} [defaultValue=false] - Default value
265
- * @returns {Promise<boolean>} User's answer
266
- */
267
- async confirm(message, defaultValue = false) {
268
- return this.prompt({
269
- type: 'confirm',
270
- message,
271
- initial: defaultValue
272
- });
273
- }
274
-
275
- /**
276
- * Prompt for text input
277
- * @param {string} message - Input message
278
- * @param {string} [defaultValue] - Default value
279
- * @param {Function} [validate] - Validation function
280
- * @returns {Promise<string>} User's input
281
- */
282
- async input(message, defaultValue, validate) {
283
- return this.prompt({
284
- type: 'input',
285
- message,
286
- initial: defaultValue,
287
- validate
288
- });
289
- }
290
-
291
- /**
292
- * Prompt for selection from a list
293
- * @param {string} message - Selection message
294
- * @param {Array<Object|string>} choices - Array of choices
295
- * @param {any} [defaultValue] - Default value
296
- * @returns {Promise<any>} Selected value
297
- */
298
- async select(message, choices, defaultValue) {
299
- // Normalize choices to {name, value} format
300
- const normalizedChoices = choices.map(choice => {
301
- if (typeof choice === 'string') {
302
- return { name: choice, value: choice };
303
- }
304
- return choice;
305
- });
306
-
307
- return this.prompt({
308
- type: 'select',
309
- message,
310
- choices: normalizedChoices,
311
- initial: defaultValue
312
- });
313
- }
314
-
315
- /**
316
- * Prompt for multiple selections
317
- * @param {string} message - Selection message
318
- * @param {Array<Object|string>} choices - Array of choices
319
- * @returns {Promise<Array>} Selected values
320
- */
321
- async checkbox(message, choices) {
322
- // Normalize choices to {name, value, checked} format
323
- const normalizedChoices = choices.map(choice => {
324
- if (typeof choice === 'string') {
325
- return { name: choice, value: choice, checked: false };
326
- }
327
- return { ...choice, checked: choice.checked || false };
328
- });
329
-
330
- return this.prompt({
331
- type: 'checkbox',
332
- message,
333
- choices: normalizedChoices
334
- });
335
- }
336
-
337
- /**
338
- * Prompt for password input
339
- * @param {string} message - Password prompt message
340
- * @param {Function} [validate] - Validation function
341
- * @returns {Promise<string>} User's password
342
- */
343
- async password(message, validate) {
344
- return this.prompt({
345
- type: 'password',
346
- message,
347
- validate
348
- });
349
- }
350
-
351
- /**
352
- * Display a progress bar
353
- * @param {number} current - Current progress (0-100)
354
- * @param {number} total - Total (usually 100)
355
- * @param {string} [message] - Optional message
356
- */
357
- progress(current, total = 100, message = '') {
358
- const percentage = Math.round((current / total) * 100);
359
- const barLength = 40;
360
- const filledLength = Math.round((barLength * current) / total);
361
- const bar = '█'.repeat(filledLength) + '░'.repeat(barLength - filledLength);
362
-
363
- process.stdout.write(
364
- `\r${chalk.cyan(bar)} ${chalk.bold(`${percentage}%`)} ${message}`
365
- );
366
-
367
- if (current >= total) {
368
- console.log(''); // New line when complete
369
- }
370
- }
371
-
372
- /**
373
- * Log raw message without formatting (for compatibility)
374
- * @param {...any} args - Arguments to log
375
- */
376
- log(...args) {
377
- console.log(...args);
378
- }
379
- }
380
-
381
- // Export a singleton instance
382
- module.exports = new Output();
@@ -1,75 +0,0 @@
1
- /**
2
- * CLI Provider Helper
3
- *
4
- * Loads the appDefinition from the user's project and resolves the
5
- * corresponding provider plugin package. Used by CLI commands to
6
- * delegate to the correct provider (AWS, Netlify, etc.).
7
- */
8
- const path = require('path');
9
- const fs = require('fs');
10
-
11
- /**
12
- * Load the appDefinition and resolve the provider plugin for CLI commands.
13
- *
14
- * This is a lightweight loader for the CLI context — it reads the backend
15
- * index.js directly (no need for the full core app-definition-loader which
16
- * searches for the nearest backend package.json at runtime).
17
- *
18
- * @param {Object} [options]
19
- * @param {string} [options.cwd] - Working directory (default: process.cwd())
20
- * @returns {{ appDefinition: Object, provider: Object, providerName: string } | null}
21
- * Returns null if no appDefinition is found (caller should fall back to default behavior).
22
- */
23
- function loadProviderForCli(options = {}) {
24
- const cwd = options.cwd || process.cwd();
25
- const appDefinition = loadCliAppDefinition(cwd);
26
-
27
- if (!appDefinition) {
28
- return null;
29
- }
30
-
31
- const providerName = appDefinition.provider || 'aws';
32
-
33
- // For 'aws', return null provider — CLI commands fall back to existing behavior
34
- if (providerName === 'aws') {
35
- return { appDefinition, provider: null, providerName: 'aws' };
36
- }
37
-
38
- // For other providers, resolve the package
39
- const {
40
- resolveProvider,
41
- } = require('@friggframework/core/providers/resolve-provider');
42
-
43
- const provider = resolveProvider(appDefinition);
44
- return { appDefinition, provider, providerName };
45
- }
46
-
47
- /**
48
- * Load the appDefinition from the backend directory.
49
- * Tries backend/index.js then index.js in the current directory.
50
- *
51
- * @param {string} cwd
52
- * @returns {Object|null}
53
- */
54
- function loadCliAppDefinition(cwd) {
55
- // Try backend/index.js first (standard Frigg app structure)
56
- const backendIndexPath = path.join(cwd, 'backend', 'index.js');
57
- const rootIndexPath = path.join(cwd, 'index.js');
58
-
59
- for (const indexPath of [backendIndexPath, rootIndexPath]) {
60
- if (fs.existsSync(indexPath)) {
61
- try {
62
- const exported = require(indexPath);
63
- if (exported.Definition) {
64
- return exported.Definition;
65
- }
66
- } catch {
67
- // Failed to load, try next
68
- }
69
- }
70
- }
71
-
72
- return null;
73
- }
74
-
75
- module.exports = { loadProviderForCli, loadCliAppDefinition };
@@ -1,205 +0,0 @@
1
- const path = require('path');
2
- const fs = require('fs');
3
- const os = require('os');
4
- const {
5
- createValidateCommand,
6
- formatConsoleOutput,
7
- autoDetectFriggApp,
8
- findBackendPathInDir,
9
- findBackendPath
10
- } = require('../../adapters/cli/validate-command');
11
- const { ValidationResult } = require('../../domain/entities/validation-result');
12
- const { ValidationError } = require('../../domain/value-objects/validation-error');
13
- const { FixSuggestion } = require('../../domain/value-objects/fix-suggestion');
14
- const { Command } = require('commander');
15
-
16
- describe('validateCommand', () => {
17
- let mockOutput;
18
-
19
- beforeEach(() => {
20
- mockOutput = {
21
- info: jest.fn(),
22
- success: jest.fn(),
23
- error: jest.fn(),
24
- warn: jest.fn(),
25
- log: jest.fn()
26
- };
27
- });
28
-
29
- describe('createValidateCommand', () => {
30
- it('registers validate command on program', () => {
31
- const program = new Command();
32
- createValidateCommand(program);
33
- const validateCmd = program.commands.find(c => c.name() === 'validate');
34
- expect(validateCmd).toBeDefined();
35
- });
36
-
37
- it('has required options', () => {
38
- const program = new Command();
39
- createValidateCommand(program);
40
- const validateCmd = program.commands.find(c => c.name() === 'validate');
41
- const options = validateCmd.options.map(o => o.long);
42
- expect(options).toContain('--format');
43
- expect(options).toContain('--verbose');
44
- });
45
- });
46
-
47
- describe('formatConsoleOutput', () => {
48
- it('outputs success for valid result', () => {
49
- const result = ValidationResult.create();
50
- formatConsoleOutput(result, {}, mockOutput);
51
- expect(mockOutput.success).toHaveBeenCalled();
52
- });
53
-
54
- it('outputs errors for invalid result', () => {
55
- const error = ValidationError.create({
56
- path: 'integrations',
57
- message: 'Missing integrations',
58
- severity: 'error'
59
- });
60
- const result = ValidationResult.create({ errors: [error] });
61
- formatConsoleOutput(result, {}, mockOutput);
62
- const logCalls = mockOutput.log.mock.calls.flat().join(' ');
63
- expect(logCalls).toContain('Missing integrations');
64
- });
65
-
66
- it('outputs warnings', () => {
67
- const warning = ValidationError.create({
68
- path: 'database',
69
- message: 'No database configured',
70
- severity: 'warning'
71
- });
72
- const result = ValidationResult.create({ errors: [warning] });
73
- formatConsoleOutput(result, {}, mockOutput);
74
- expect(mockOutput.warn).toHaveBeenCalled();
75
- });
76
-
77
- it('shows fix suggestions in verbose mode', () => {
78
- const fix = FixSuggestion.create({ action: 'add', description: 'Add database configuration' });
79
- const error = ValidationError.create({
80
- path: 'database',
81
- message: 'Missing config',
82
- severity: 'error',
83
- fix
84
- });
85
- const result = ValidationResult.create({ errors: [error] });
86
- formatConsoleOutput(result, { verbose: true }, mockOutput);
87
- const logCalls = mockOutput.log.mock.calls.flat().join(' ');
88
- expect(logCalls).toContain('Add database configuration');
89
- });
90
- });
91
-
92
- describe('findBackendPathInDir', () => {
93
- let tmpDir;
94
-
95
- beforeEach(() => {
96
- tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'frigg-test-'));
97
- });
98
-
99
- afterEach(() => {
100
- fs.rmSync(tmpDir, { recursive: true, force: true });
101
- });
102
-
103
- it('finds backend/index.js', () => {
104
- const backendDir = path.join(tmpDir, 'backend');
105
- fs.mkdirSync(backendDir);
106
- fs.writeFileSync(path.join(backendDir, 'index.js'), 'module.exports = {}');
107
- expect(findBackendPathInDir(tmpDir)).toBe(backendDir);
108
- });
109
-
110
- it('finds index.js with integrations keyword', () => {
111
- fs.writeFileSync(path.join(tmpDir, 'index.js'), 'module.exports = { integrations: [] }');
112
- expect(findBackendPathInDir(tmpDir)).toBe(tmpDir);
113
- });
114
-
115
- it('finds index.js with Definition keyword', () => {
116
- fs.writeFileSync(path.join(tmpDir, 'index.js'), 'module.exports = { Definition: {} }');
117
- expect(findBackendPathInDir(tmpDir)).toBe(tmpDir);
118
- });
119
-
120
- it('returns null for non-frigg directory', () => {
121
- fs.writeFileSync(path.join(tmpDir, 'index.js'), 'console.log("hello")');
122
- expect(findBackendPathInDir(tmpDir)).toBeNull();
123
- });
124
-
125
- it('returns null for empty directory', () => {
126
- expect(findBackendPathInDir(tmpDir)).toBeNull();
127
- });
128
- });
129
-
130
- describe('findBackendPath', () => {
131
- let tmpDir;
132
-
133
- beforeEach(() => {
134
- tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'frigg-test-'));
135
- });
136
-
137
- afterEach(() => {
138
- fs.rmSync(tmpDir, { recursive: true, force: true });
139
- });
140
-
141
- it('finds backend subdirectory with package.json', () => {
142
- const backendDir = path.join(tmpDir, 'backend');
143
- fs.mkdirSync(backendDir);
144
- fs.writeFileSync(path.join(backendDir, 'package.json'), '{}');
145
- expect(findBackendPath(tmpDir)).toBe(backendDir);
146
- });
147
-
148
- it('finds backend subdirectory with index.js', () => {
149
- const backendDir = path.join(tmpDir, 'backend');
150
- fs.mkdirSync(backendDir);
151
- fs.writeFileSync(path.join(backendDir, 'index.js'), 'module.exports = {}');
152
- expect(findBackendPath(tmpDir)).toBe(backendDir);
153
- });
154
-
155
- it('returns path itself if it has package.json', () => {
156
- fs.writeFileSync(path.join(tmpDir, 'package.json'), '{}');
157
- expect(findBackendPath(tmpDir)).toBe(tmpDir);
158
- });
159
-
160
- it('returns null for non-backend directory', () => {
161
- expect(findBackendPath(tmpDir)).toBeNull();
162
- });
163
- });
164
-
165
- describe('autoDetectFriggApp', () => {
166
- let tmpDir;
167
-
168
- beforeEach(() => {
169
- tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'frigg-test-'));
170
- });
171
-
172
- afterEach(() => {
173
- fs.rmSync(tmpDir, { recursive: true, force: true });
174
- });
175
-
176
- it('detects frigg app in current directory', () => {
177
- const backendDir = path.join(tmpDir, 'backend');
178
- fs.mkdirSync(backendDir);
179
- fs.writeFileSync(path.join(backendDir, 'index.js'), 'module.exports = {}');
180
- const result = autoDetectFriggApp(tmpDir);
181
- expect(result).toEqual({
182
- appRoot: tmpDir,
183
- backendPath: backendDir
184
- });
185
- });
186
-
187
- it('detects frigg app in parent directory', () => {
188
- const backendDir = path.join(tmpDir, 'backend');
189
- const subDir = path.join(tmpDir, 'src', 'components');
190
- fs.mkdirSync(backendDir);
191
- fs.mkdirSync(subDir, { recursive: true });
192
- fs.writeFileSync(path.join(backendDir, 'index.js'), 'module.exports = {}');
193
- const result = autoDetectFriggApp(subDir);
194
- expect(result).toEqual({
195
- appRoot: tmpDir,
196
- backendPath: backendDir
197
- });
198
- });
199
-
200
- it('returns null when no frigg app found', () => {
201
- const result = autoDetectFriggApp(tmpDir);
202
- expect(result).toBeNull();
203
- });
204
- });
205
- });