@friggframework/devtools 2.0.0--canary.545.e256e95.0 → 2.0.0--canary.553.dc5f898.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 (129) 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/integration/integration-builder.js +3 -2
  30. package/infrastructure/domains/integration/integration-builder.test.js +54 -2
  31. package/infrastructure/domains/networking/vpc-builder.test.js +4 -2
  32. package/infrastructure/domains/networking/vpc-resolver.test.js +1 -1
  33. package/infrastructure/domains/shared/resource-discovery.js +5 -5
  34. package/infrastructure/domains/shared/types/app-definition.js +0 -35
  35. package/infrastructure/domains/shared/types/discovery-result.test.js +1 -1
  36. package/infrastructure/domains/shared/utilities/base-definition-factory.js +1 -10
  37. package/infrastructure/domains/shared/utilities/base-definition-factory.test.js +2 -2
  38. package/infrastructure/infrastructure-composer.js +0 -2
  39. package/infrastructure/infrastructure-composer.test.js +6 -5
  40. package/management-ui/README.md +109 -245
  41. package/package.json +7 -8
  42. package/frigg-cli/__tests__/application/use-cases/AddApiModuleToIntegrationUseCase.test.js +0 -326
  43. package/frigg-cli/__tests__/application/use-cases/CreateApiModuleUseCase.test.js +0 -337
  44. package/frigg-cli/__tests__/domain/entities/ApiModule.test.js +0 -373
  45. package/frigg-cli/__tests__/domain/entities/AppDefinition.test.js +0 -313
  46. package/frigg-cli/__tests__/domain/services/IntegrationValidator.test.js +0 -269
  47. package/frigg-cli/__tests__/domain/value-objects/IntegrationName.test.js +0 -82
  48. package/frigg-cli/__tests__/infrastructure/adapters/IntegrationJsUpdater.test.js +0 -408
  49. package/frigg-cli/__tests__/infrastructure/repositories/FileSystemApiModuleRepository.test.js +0 -583
  50. package/frigg-cli/__tests__/infrastructure/repositories/FileSystemAppDefinitionRepository.test.js +0 -314
  51. package/frigg-cli/__tests__/infrastructure/repositories/FileSystemIntegrationRepository.test.js +0 -383
  52. package/frigg-cli/__tests__/unit/commands/init.test.js +0 -406
  53. package/frigg-cli/__tests__/unit/commands/provider-dispatch.test.js +0 -383
  54. package/frigg-cli/__tests__/unit/commands/repair.test.js +0 -275
  55. package/frigg-cli/__tests__/unit/start-command/application/RunPreflightChecksUseCase.test.js +0 -411
  56. package/frigg-cli/__tests__/unit/start-command/infrastructure/DatabaseAdapter.test.js +0 -405
  57. package/frigg-cli/__tests__/unit/start-command/infrastructure/DockerAdapter.test.js +0 -496
  58. package/frigg-cli/__tests__/unit/start-command/presentation/InteractivePromptAdapter.test.js +0 -474
  59. package/frigg-cli/__tests__/unit/utils/output.test.js +0 -196
  60. package/frigg-cli/application/use-cases/AddApiModuleToIntegrationUseCase.js +0 -93
  61. package/frigg-cli/application/use-cases/CreateApiModuleUseCase.js +0 -93
  62. package/frigg-cli/application/use-cases/CreateIntegrationUseCase.js +0 -103
  63. package/frigg-cli/container.js +0 -172
  64. package/frigg-cli/docs/OUTPUT_MIGRATION_GUIDE.md +0 -286
  65. package/frigg-cli/domain/entities/ApiModule.js +0 -272
  66. package/frigg-cli/domain/entities/AppDefinition.js +0 -227
  67. package/frigg-cli/domain/entities/Integration.js +0 -198
  68. package/frigg-cli/domain/exceptions/DomainException.js +0 -24
  69. package/frigg-cli/domain/ports/IApiModuleRepository.js +0 -53
  70. package/frigg-cli/domain/ports/IAppDefinitionRepository.js +0 -43
  71. package/frigg-cli/domain/ports/IIntegrationRepository.js +0 -61
  72. package/frigg-cli/domain/services/IntegrationValidator.js +0 -185
  73. package/frigg-cli/domain/value-objects/IntegrationId.js +0 -42
  74. package/frigg-cli/domain/value-objects/IntegrationName.js +0 -60
  75. package/frigg-cli/domain/value-objects/SemanticVersion.js +0 -70
  76. package/frigg-cli/infrastructure/UnitOfWork.js +0 -46
  77. package/frigg-cli/infrastructure/adapters/BackendJsUpdater.js +0 -197
  78. package/frigg-cli/infrastructure/adapters/FileSystemAdapter.js +0 -224
  79. package/frigg-cli/infrastructure/adapters/IntegrationJsUpdater.js +0 -249
  80. package/frigg-cli/infrastructure/adapters/SchemaValidator.js +0 -92
  81. package/frigg-cli/infrastructure/repositories/FileSystemApiModuleRepository.js +0 -373
  82. package/frigg-cli/infrastructure/repositories/FileSystemAppDefinitionRepository.js +0 -116
  83. package/frigg-cli/infrastructure/repositories/FileSystemIntegrationRepository.js +0 -277
  84. package/frigg-cli/package-lock.json +0 -16226
  85. package/frigg-cli/start-command/application/RunPreflightChecksUseCase.js +0 -376
  86. package/frigg-cli/start-command/infrastructure/DatabaseAdapter.js +0 -591
  87. package/frigg-cli/start-command/infrastructure/DockerAdapter.js +0 -306
  88. package/frigg-cli/start-command/presentation/InteractivePromptAdapter.js +0 -329
  89. package/frigg-cli/templates/backend/.env.example +0 -62
  90. package/frigg-cli/templates/backend/.eslintrc.json +0 -12
  91. package/frigg-cli/templates/backend/.prettierrc +0 -6
  92. package/frigg-cli/templates/backend/docker-compose.yml +0 -22
  93. package/frigg-cli/templates/backend/index.js +0 -96
  94. package/frigg-cli/templates/backend/infrastructure.js +0 -12
  95. package/frigg-cli/templates/backend/jest.config.js +0 -17
  96. package/frigg-cli/templates/backend/package.json +0 -50
  97. package/frigg-cli/templates/backend/src/api-modules/.gitkeep +0 -10
  98. package/frigg-cli/templates/backend/src/base/.gitkeep +0 -7
  99. package/frigg-cli/templates/backend/src/integrations/.gitkeep +0 -10
  100. package/frigg-cli/templates/backend/src/integrations/ExampleIntegration.js +0 -65
  101. package/frigg-cli/templates/backend/src/utils/.gitkeep +0 -7
  102. package/frigg-cli/templates/backend/test/setup.js +0 -30
  103. package/frigg-cli/templates/backend/ui-extensions/.gitkeep +0 -0
  104. package/frigg-cli/templates/backend/ui-extensions/README.md +0 -77
  105. package/frigg-cli/utils/__tests__/provider-helper.test.js +0 -55
  106. package/frigg-cli/utils/__tests__/repo-detection.test.js +0 -436
  107. package/frigg-cli/utils/output.js +0 -382
  108. package/frigg-cli/utils/provider-helper.js +0 -75
  109. package/frigg-cli/validate-command/__tests__/adapters/validate-command.test.js +0 -205
  110. package/frigg-cli/validate-command/__tests__/application/validate-app-use-case.test.js +0 -104
  111. package/frigg-cli/validate-command/__tests__/domain/fix-suggestion.test.js +0 -153
  112. package/frigg-cli/validate-command/__tests__/domain/validation-error.test.js +0 -162
  113. package/frigg-cli/validate-command/__tests__/domain/validation-result.test.js +0 -152
  114. package/frigg-cli/validate-command/__tests__/infrastructure/api-module-validator.test.js +0 -332
  115. package/frigg-cli/validate-command/__tests__/infrastructure/app-definition-validator.test.js +0 -191
  116. package/frigg-cli/validate-command/__tests__/infrastructure/integration-class-validator.test.js +0 -146
  117. package/frigg-cli/validate-command/__tests__/infrastructure/template-validation.test.js +0 -155
  118. package/frigg-cli/validate-command/adapters/cli/validate-command.js +0 -199
  119. package/frigg-cli/validate-command/application/use-cases/validate-app-use-case.js +0 -35
  120. package/frigg-cli/validate-command/domain/entities/validation-result.js +0 -74
  121. package/frigg-cli/validate-command/domain/value-objects/fix-suggestion.js +0 -74
  122. package/frigg-cli/validate-command/domain/value-objects/validation-error.js +0 -68
  123. package/frigg-cli/validate-command/infrastructure/validators/api-module-validator.js +0 -181
  124. package/frigg-cli/validate-command/infrastructure/validators/app-definition-validator.js +0 -145
  125. package/frigg-cli/validate-command/infrastructure/validators/integration-class-validator.js +0 -113
  126. package/infrastructure/domains/admin-scripts/admin-script-builder.js +0 -200
  127. package/infrastructure/domains/admin-scripts/admin-script-builder.test.js +0 -499
  128. package/infrastructure/domains/admin-scripts/index.js +0 -5
  129. 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
- });