@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,286 +0,0 @@
1
- # Output Class Migration Guide
2
-
3
- This guide shows how to migrate existing CLI commands to use the new unified `Output` class.
4
-
5
- ## Why Migrate?
6
-
7
- The unified `Output` class provides:
8
- - **Consistency**: Same UI patterns across all commands
9
- - **Better UX**: Spinners, progress bars, formatted tables
10
- - **Maintainability**: Single place to update UI behavior
11
- - **Testing**: Easier to mock and test
12
-
13
- ## Before vs After
14
-
15
- ### Before (Inconsistent)
16
-
17
- ```javascript
18
- // install-command/index.js (OLD - plain console.log)
19
- function logError(message, error) {
20
- console.error(message, error);
21
- }
22
-
23
- function logSuccess(message) {
24
- console.log(message);
25
- }
26
-
27
- // Some other command (OLD - using chalk directly)
28
- const chalk = require('chalk');
29
- console.log(chalk.green('✓ Success'));
30
- console.error(chalk.red('✗ Failed'));
31
-
32
- // repair-command (OLD - using readline)
33
- const readline = require('readline');
34
- const rl = readline.createInterface({
35
- input: process.stdin,
36
- output: process.stdout
37
- });
38
- rl.question('Select option: ', (answer) => {
39
- // ...
40
- });
41
- ```
42
-
43
- ### After (Consistent)
44
-
45
- ```javascript
46
- // All commands use the same output utility
47
- const output = require('./utils/output');
48
-
49
- output.success('Operation completed');
50
- output.error('Operation failed', error);
51
-
52
- const spinner = output.spinner('Installing...');
53
- // do work
54
- spinner.succeed('Installed successfully');
55
-
56
- const answer = await output.confirm('Continue with installation?');
57
- ```
58
-
59
- ## Migration Steps
60
-
61
- ### Step 1: Import Output
62
-
63
- Replace all imports of `chalk`, `console`, and `readline`:
64
-
65
- ```diff
66
- - const chalk = require('chalk');
67
- - const readline = require('readline');
68
- + const output = require('../utils/output');
69
- ```
70
-
71
- ### Step 2: Replace Console Methods
72
-
73
- | Old | New |
74
- |-----|-----|
75
- | `console.log('Success')` | `output.success('Success')` |
76
- | `console.error('Error')` | `output.error('Error')` |
77
- | `console.info('Info')` | `output.info('Info')` |
78
- | `console.warn('Warning')` | `output.warn('Warning')` |
79
- | `console.log(chalk.green('✓ Done'))` | `output.success('Done')` |
80
- | `console.error(chalk.red('✗ Failed'))` | `output.error('Failed')` |
81
-
82
- ### Step 3: Replace Chalk Usage
83
-
84
- ```diff
85
- - console.log(chalk.blue('Starting...'));
86
- + output.info('Starting...');
87
-
88
- - console.log(chalk.bold('=== Header ==='));
89
- + output.header('Header');
90
-
91
- - console.log(chalk.yellow('⚠ Warning'));
92
- + output.warn('Warning');
93
- ```
94
-
95
- ### Step 4: Replace Inquirer Prompts
96
-
97
- ```diff
98
- - const { confirm } = require('@inquirer/prompts');
99
- - const answer = await confirm({ message: 'Continue?' });
100
- + const answer = await output.confirm('Continue?');
101
-
102
- - const { select } = require('@inquirer/prompts');
103
- - const choice = await select({
104
- - message: 'Select option',
105
- - choices: ['Option 1', 'Option 2']
106
- - });
107
- + const choice = await output.select('Select option', ['Option 1', 'Option 2']);
108
- ```
109
-
110
- ### Step 5: Replace Readline (repair-command)
111
-
112
- ```diff
113
- - const readline = require('readline');
114
- - const rl = readline.createInterface({
115
- - input: process.stdin,
116
- - output: process.stdout
117
- - });
118
- - rl.question('Select option: ', (answer) => {
119
- - // handle answer
120
- - rl.close();
121
- - });
122
- + const answer = await output.input('Select option:');
123
- + // handle answer (no need to close)
124
- ```
125
-
126
- ### Step 6: Add Spinners for Long Operations
127
-
128
- ```diff
129
- - console.log('Installing dependencies...');
130
- - await installDependencies();
131
- - console.log('Done');
132
- + const spinner = output.spinner('Installing dependencies...');
133
- + await installDependencies();
134
- + spinner.succeed('Dependencies installed');
135
- ```
136
-
137
- ### Step 7: Add Progress Bars
138
-
139
- ```diff
140
- - console.log(`Progress: ${i}/${total}`);
141
- + output.progress(i, total, 'Processing files...');
142
- ```
143
-
144
- ### Step 8: Display Tables
145
-
146
- ```diff
147
- - modules.forEach(mod => {
148
- - console.log(`${mod.name}\t${mod.version}\t${mod.status}`);
149
- - });
150
- + output.table(modules, ['name', 'version', 'status']);
151
- ```
152
-
153
- ## Real Example: install-command
154
-
155
- ### Before
156
-
157
- ```javascript
158
- // install-command/logger.js
159
- function logError(message, error) {
160
- console.error(message, error);
161
- if (error && error.stack) {
162
- console.error(error.stack);
163
- }
164
- }
165
-
166
- function logSuccess(message) {
167
- console.log(message);
168
- }
169
-
170
- function logInfo(message) {
171
- console.log(message);
172
- }
173
-
174
- module.exports = {
175
- logError,
176
- logSuccess,
177
- logInfo
178
- };
179
-
180
- // install-command/index.js
181
- const logger = require('./logger');
182
-
183
- logger.logInfo('Starting installation...');
184
- // ...
185
- logger.logSuccess('Module installed successfully');
186
- ```
187
-
188
- ### After
189
-
190
- ```javascript
191
- // install-command/index.js
192
- const output = require('../utils/output');
193
-
194
- const spinner = output.spinner('Installing module...');
195
- try {
196
- // ... do installation
197
- spinner.succeed('Module installed successfully');
198
- } catch (error) {
199
- spinner.fail('Installation failed');
200
- output.error('Failed to install module', error);
201
- process.exit(1);
202
- }
203
- ```
204
-
205
- **Result**: Delete `install-command/logger.js` (no longer needed!)
206
-
207
- ## Commands to Migrate
208
-
209
- Priority order based on usage and inconsistency:
210
-
211
- 1. **install-command** (HIGH) - Uses plain console.log, has trivial logger wrapper
212
- 2. **repair-command** (HIGH) - Uses readline instead of inquirer
213
- 3. **doctor-command** (MEDIUM) - Long-running, needs spinners
214
- 4. **deploy-command** (MEDIUM) - Long-running, needs progress indication
215
- 5. **start-command** (LOW) - Already uses chalk consistently
216
- 6. **generate-command** (LOW) - Uses inquirer, but inconsistent colors
217
- 7. **build-command** (LOW) - Simple command, less output
218
-
219
- ## Testing Your Migration
220
-
221
- After migrating a command:
222
-
223
- 1. **Run the command** manually to verify output looks correct
224
- 2. **Update tests** to mock `output` instead of `console`/`chalk`/`inquirer`
225
- 3. **Check for** color consistency, spinner behavior, error messages
226
-
227
- ### Test Example
228
-
229
- ```javascript
230
- // Before
231
- jest.spyOn(console, 'log');
232
- await myCommand();
233
- expect(console.log).toHaveBeenCalledWith(expect.stringContaining('Success'));
234
-
235
- // After
236
- const output = require('../utils/output');
237
- jest.spyOn(output, 'success');
238
- await myCommand();
239
- expect(output.success).toHaveBeenCalledWith('Operation completed');
240
- ```
241
-
242
- ## Output API Reference
243
-
244
- ### Messages
245
- - `output.success(message)` - Green checkmark + message
246
- - `output.error(message, error?)` - Red X + message (+ stack trace if DEBUG=1)
247
- - `output.info(message)` - Blue info icon + message
248
- - `output.warn(message)` - Yellow warning icon + message
249
- - `output.debug(message)` - Gray message (only if DEBUG=1)
250
-
251
- ### Formatting
252
- - `output.header(title)` - Bold cyan title with underline
253
- - `output.separator()` - Gray horizontal line
254
- - `output.newline()` - Blank line
255
- - `output.table(data, columns?)` - Formatted table
256
- - `output.keyValue(object)` - Key-value pairs
257
- - `output.json(data, indent?)` - Syntax-highlighted JSON
258
-
259
- ### Interactive
260
- - `output.confirm(message, default?)` - Yes/no question
261
- - `output.input(message, default?, validate?)` - Text input
262
- - `output.select(message, choices, default?)` - Single selection
263
- - `output.checkbox(message, choices)` - Multiple selection
264
- - `output.password(message, validate?)` - Password input
265
-
266
- ### Progress
267
- - `output.spinner(text)` - Returns {update, succeed, fail, stop}
268
- - `output.progress(current, total, message?)` - Progress bar
269
-
270
- ### Compatibility
271
- - `output.log(...args)` - Raw console.log (for gradual migration)
272
-
273
- ## Benefits After Migration
274
-
275
- - ✅ **32% less code** - Remove logger wrappers and boilerplate
276
- - ✅ **100% consistency** - All commands look and feel the same
277
- - ✅ **Better UX** - Spinners, progress bars, formatted tables
278
- - ✅ **Easier testing** - Mock one module instead of many
279
- - ✅ **Maintainable** - Update UI in one place
280
-
281
- ## Questions?
282
-
283
- See:
284
- - `utils/output.js` - Full implementation
285
- - `__tests__/unit/utils/output.test.js` - Test examples
286
- - `FRIGG_CLI_ANALYSIS_REPORT.md` - Why we created this
@@ -1,272 +0,0 @@
1
- const {DomainException} = require('../exceptions/DomainException');
2
- const {SemanticVersion} = require('../value-objects/SemanticVersion');
3
-
4
- /**
5
- * ApiModule Entity
6
- *
7
- * Represents an API module that can be used by integrations
8
- * API modules are reusable API clients for external services
9
- */
10
- class ApiModule {
11
- constructor(props) {
12
- this.name = props.name; // kebab-case name
13
- this.version = props.version instanceof SemanticVersion ?
14
- props.version : new SemanticVersion(props.version || '1.0.0');
15
- this.displayName = props.displayName || this._generateDisplayName();
16
- this.description = props.description || '';
17
- this.author = props.author || '';
18
- this.license = props.license || 'UNLICENSED';
19
- this.apiConfig = props.apiConfig || {
20
- baseUrl: '',
21
- authType: 'oauth2',
22
- version: 'v1'
23
- };
24
- this.entities = props.entities || {}; // Database entities this module needs
25
- this.scopes = props.scopes || []; // OAuth scopes required
26
- this.credentials = props.credentials || []; // Required credentials
27
- this.endpoints = props.endpoints || {}; // API endpoints
28
- this.createdAt = props.createdAt || new Date();
29
- this.updatedAt = props.updatedAt || new Date();
30
- }
31
-
32
- /**
33
- * Factory method to create a new ApiModule
34
- */
35
- static create(props) {
36
- if (!props.name) {
37
- throw new DomainException('API module name is required');
38
- }
39
-
40
- // Validate name format
41
- const namePattern = /^[a-z0-9][a-z0-9-]*[a-z0-9]$/;
42
- if (!namePattern.test(props.name)) {
43
- throw new DomainException('API module name must be kebab-case');
44
- }
45
-
46
- // Validate authType is provided
47
- if (!props.apiConfig || !props.apiConfig.authType) {
48
- throw new DomainException('Authentication type is required');
49
- }
50
-
51
- return new ApiModule(props);
52
- }
53
-
54
- /**
55
- * Reconstruct ApiModule from plain object
56
- */
57
- static fromObject(obj) {
58
- return new ApiModule({
59
- ...obj,
60
- version: obj.version,
61
- createdAt: new Date(obj.createdAt),
62
- updatedAt: new Date(obj.updatedAt)
63
- });
64
- }
65
-
66
- /**
67
- * Add an entity configuration
68
- * Entities are database records that store API credentials and state
69
- *
70
- * @param {string} entityName - Entity name (e.g., 'credential', 'user')
71
- * @param {object} config - Entity configuration
72
- */
73
- addEntity(entityName, config = {}) {
74
- if (this.hasEntity(entityName)) {
75
- throw new DomainException(`Entity '${entityName}' already exists`);
76
- }
77
-
78
- this.entities[entityName] = {
79
- type: entityName,
80
- label: config.label || entityName,
81
- required: config.required !== false,
82
- fields: config.fields || [],
83
- ...config
84
- };
85
-
86
- this.updatedAt = new Date();
87
- return this;
88
- }
89
-
90
- /**
91
- * Check if entity exists
92
- */
93
- hasEntity(entityName) {
94
- return entityName in this.entities;
95
- }
96
-
97
- /**
98
- * Add an endpoint definition
99
- */
100
- addEndpoint(name, config) {
101
- if (this.hasEndpoint(name)) {
102
- throw new DomainException(`Endpoint '${name}' already exists`);
103
- }
104
-
105
- this.endpoints[name] = {
106
- method: config.method || 'GET',
107
- path: config.path,
108
- description: config.description || '',
109
- parameters: config.parameters || [],
110
- response: config.response || {},
111
- ...config
112
- };
113
-
114
- this.updatedAt = new Date();
115
- return this;
116
- }
117
-
118
- /**
119
- * Check if endpoint exists
120
- */
121
- hasEndpoint(name) {
122
- return name in this.endpoints;
123
- }
124
-
125
- /**
126
- * Add required OAuth scope
127
- */
128
- addScope(scope) {
129
- if (this.scopes.includes(scope)) {
130
- throw new DomainException(`Scope '${scope}' already exists`);
131
- }
132
- this.scopes.push(scope);
133
- this.updatedAt = new Date();
134
- return this;
135
- }
136
-
137
- /**
138
- * Add required credential
139
- */
140
- addCredential(name, config = {}) {
141
- const existing = this.credentials.find(c => c.name === name);
142
- if (existing) {
143
- throw new DomainException(`Credential '${name}' already exists`);
144
- }
145
-
146
- this.credentials.push({
147
- name,
148
- type: config.type || 'string',
149
- required: config.required !== false,
150
- description: config.description || '',
151
- example: config.example || '',
152
- envVar: config.envVar || '',
153
- ...config
154
- });
155
-
156
- this.updatedAt = new Date();
157
- return this;
158
- }
159
-
160
- /**
161
- * Check if credential exists
162
- */
163
- hasCredential(name) {
164
- return this.credentials.some(c => c.name === name);
165
- }
166
-
167
- /**
168
- * Validate API module business rules
169
- */
170
- validate() {
171
- const errors = [];
172
-
173
- // Name validation (kebab-case)
174
- if (!this.name || this.name.trim().length === 0) {
175
- errors.push('API module name is required');
176
- }
177
-
178
- const namePattern = /^[a-z0-9][a-z0-9-]*[a-z0-9]$/;
179
- if (this.name && !namePattern.test(this.name)) {
180
- errors.push('API module name must be kebab-case');
181
- }
182
-
183
- // Display name validation
184
- if (!this.displayName || this.displayName.trim().length === 0) {
185
- errors.push('Display name is required');
186
- }
187
-
188
- // Description validation
189
- if (this.description && this.description.length > 1000) {
190
- errors.push('Description must be 1000 characters or less');
191
- }
192
-
193
- // API config validation
194
- if (!this.apiConfig.baseUrl) {
195
- // Warning: base URL should be provided, but not required at creation
196
- }
197
-
198
- // Auth type validation
199
- if (!this.apiConfig.authType || this.apiConfig.authType.trim().length === 0) {
200
- errors.push('Authentication type is required');
201
- } else {
202
- const validAuthTypes = ['oauth2', 'api-key', 'basic', 'token', 'custom'];
203
- if (!validAuthTypes.includes(this.apiConfig.authType)) {
204
- errors.push(`Invalid auth type. Must be one of: ${validAuthTypes.join(', ')}`);
205
- }
206
- }
207
-
208
- return {
209
- isValid: errors.length === 0,
210
- errors
211
- };
212
- }
213
-
214
- /**
215
- * Convert to plain object
216
- */
217
- toObject() {
218
- return {
219
- name: this.name,
220
- version: this.version.value,
221
- displayName: this.displayName,
222
- description: this.description,
223
- author: this.author,
224
- license: this.license,
225
- apiConfig: this.apiConfig,
226
- entities: this.entities,
227
- scopes: this.scopes,
228
- credentials: this.credentials,
229
- endpoints: this.endpoints,
230
- createdAt: this.createdAt,
231
- updatedAt: this.updatedAt
232
- };
233
- }
234
-
235
- /**
236
- * Convert to JSON format (for api-module definition files)
237
- */
238
- toJSON() {
239
- return {
240
- name: this.name,
241
- version: this.version.value,
242
- display: {
243
- name: this.displayName,
244
- description: this.description
245
- },
246
- api: {
247
- baseUrl: this.apiConfig.baseUrl,
248
- authType: this.apiConfig.authType,
249
- version: this.apiConfig.version
250
- },
251
- entities: this.entities,
252
- auth: {
253
- type: this.apiConfig.authType,
254
- scopes: this.scopes,
255
- credentials: this.credentials
256
- },
257
- endpoints: this.endpoints
258
- };
259
- }
260
-
261
- /**
262
- * Generate display name from kebab-case name
263
- */
264
- _generateDisplayName() {
265
- return this.name
266
- .split('-')
267
- .map(word => word.charAt(0).toUpperCase() + word.slice(1))
268
- .join(' ');
269
- }
270
- }
271
-
272
- module.exports = {ApiModule};