@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.
- package/frigg-cli/README.md +1 -1
- package/frigg-cli/__tests__/unit/commands/build.test.js +1 -1
- package/frigg-cli/__tests__/unit/commands/doctor.test.js +2 -0
- package/frigg-cli/__tests__/unit/commands/install.test.js +19 -23
- package/frigg-cli/__tests__/unit/dependencies.test.js +2 -2
- package/frigg-cli/build-command/index.js +11 -123
- package/frigg-cli/deploy-command/index.js +1 -83
- package/frigg-cli/doctor-command/index.js +16 -37
- package/frigg-cli/generate-iam-command.js +1 -21
- package/frigg-cli/index.js +6 -21
- package/frigg-cli/index.test.js +2 -7
- package/frigg-cli/init-command/backend-first-handler.js +42 -124
- package/frigg-cli/init-command/index.js +1 -2
- package/frigg-cli/init-command/template-handler.js +3 -13
- package/frigg-cli/install-command/backend-js.js +3 -3
- package/frigg-cli/install-command/environment-variables.js +19 -16
- package/frigg-cli/install-command/environment-variables.test.js +13 -12
- package/frigg-cli/install-command/index.js +9 -14
- package/frigg-cli/install-command/integration-file.js +3 -3
- package/frigg-cli/install-command/logger.js +12 -0
- package/frigg-cli/install-command/validate-package.js +9 -5
- package/frigg-cli/jest.config.js +1 -4
- package/frigg-cli/repair-command/index.js +128 -121
- package/frigg-cli/start-command/index.js +2 -324
- package/frigg-cli/ui-command/index.js +36 -58
- package/frigg-cli/utils/repo-detection.js +37 -85
- package/infrastructure/create-frigg-infrastructure.js +0 -93
- package/infrastructure/docs/iam-policy-templates.md +1 -1
- package/infrastructure/domains/integration/integration-builder.js +3 -2
- package/infrastructure/domains/integration/integration-builder.test.js +54 -2
- package/infrastructure/domains/networking/vpc-builder.test.js +4 -2
- package/infrastructure/domains/networking/vpc-resolver.test.js +1 -1
- package/infrastructure/domains/shared/resource-discovery.js +5 -5
- package/infrastructure/domains/shared/types/app-definition.js +0 -35
- package/infrastructure/domains/shared/types/discovery-result.test.js +1 -1
- package/infrastructure/domains/shared/utilities/base-definition-factory.js +1 -10
- package/infrastructure/domains/shared/utilities/base-definition-factory.test.js +2 -2
- package/infrastructure/infrastructure-composer.js +0 -2
- package/infrastructure/infrastructure-composer.test.js +6 -5
- package/management-ui/README.md +109 -245
- package/package.json +7 -8
- package/frigg-cli/__tests__/application/use-cases/AddApiModuleToIntegrationUseCase.test.js +0 -326
- package/frigg-cli/__tests__/application/use-cases/CreateApiModuleUseCase.test.js +0 -337
- package/frigg-cli/__tests__/domain/entities/ApiModule.test.js +0 -373
- package/frigg-cli/__tests__/domain/entities/AppDefinition.test.js +0 -313
- package/frigg-cli/__tests__/domain/services/IntegrationValidator.test.js +0 -269
- package/frigg-cli/__tests__/domain/value-objects/IntegrationName.test.js +0 -82
- package/frigg-cli/__tests__/infrastructure/adapters/IntegrationJsUpdater.test.js +0 -408
- package/frigg-cli/__tests__/infrastructure/repositories/FileSystemApiModuleRepository.test.js +0 -583
- package/frigg-cli/__tests__/infrastructure/repositories/FileSystemAppDefinitionRepository.test.js +0 -314
- package/frigg-cli/__tests__/infrastructure/repositories/FileSystemIntegrationRepository.test.js +0 -383
- package/frigg-cli/__tests__/unit/commands/init.test.js +0 -406
- package/frigg-cli/__tests__/unit/commands/provider-dispatch.test.js +0 -383
- package/frigg-cli/__tests__/unit/commands/repair.test.js +0 -275
- package/frigg-cli/__tests__/unit/start-command/application/RunPreflightChecksUseCase.test.js +0 -411
- package/frigg-cli/__tests__/unit/start-command/infrastructure/DatabaseAdapter.test.js +0 -405
- package/frigg-cli/__tests__/unit/start-command/infrastructure/DockerAdapter.test.js +0 -496
- package/frigg-cli/__tests__/unit/start-command/presentation/InteractivePromptAdapter.test.js +0 -474
- package/frigg-cli/__tests__/unit/utils/output.test.js +0 -196
- package/frigg-cli/application/use-cases/AddApiModuleToIntegrationUseCase.js +0 -93
- package/frigg-cli/application/use-cases/CreateApiModuleUseCase.js +0 -93
- package/frigg-cli/application/use-cases/CreateIntegrationUseCase.js +0 -103
- package/frigg-cli/container.js +0 -172
- package/frigg-cli/docs/OUTPUT_MIGRATION_GUIDE.md +0 -286
- package/frigg-cli/domain/entities/ApiModule.js +0 -272
- package/frigg-cli/domain/entities/AppDefinition.js +0 -227
- package/frigg-cli/domain/entities/Integration.js +0 -198
- package/frigg-cli/domain/exceptions/DomainException.js +0 -24
- package/frigg-cli/domain/ports/IApiModuleRepository.js +0 -53
- package/frigg-cli/domain/ports/IAppDefinitionRepository.js +0 -43
- package/frigg-cli/domain/ports/IIntegrationRepository.js +0 -61
- package/frigg-cli/domain/services/IntegrationValidator.js +0 -185
- package/frigg-cli/domain/value-objects/IntegrationId.js +0 -42
- package/frigg-cli/domain/value-objects/IntegrationName.js +0 -60
- package/frigg-cli/domain/value-objects/SemanticVersion.js +0 -70
- package/frigg-cli/infrastructure/UnitOfWork.js +0 -46
- package/frigg-cli/infrastructure/adapters/BackendJsUpdater.js +0 -197
- package/frigg-cli/infrastructure/adapters/FileSystemAdapter.js +0 -224
- package/frigg-cli/infrastructure/adapters/IntegrationJsUpdater.js +0 -249
- package/frigg-cli/infrastructure/adapters/SchemaValidator.js +0 -92
- package/frigg-cli/infrastructure/repositories/FileSystemApiModuleRepository.js +0 -373
- package/frigg-cli/infrastructure/repositories/FileSystemAppDefinitionRepository.js +0 -116
- package/frigg-cli/infrastructure/repositories/FileSystemIntegrationRepository.js +0 -277
- package/frigg-cli/package-lock.json +0 -16226
- package/frigg-cli/start-command/application/RunPreflightChecksUseCase.js +0 -376
- package/frigg-cli/start-command/infrastructure/DatabaseAdapter.js +0 -591
- package/frigg-cli/start-command/infrastructure/DockerAdapter.js +0 -306
- package/frigg-cli/start-command/presentation/InteractivePromptAdapter.js +0 -329
- package/frigg-cli/templates/backend/.env.example +0 -62
- package/frigg-cli/templates/backend/.eslintrc.json +0 -12
- package/frigg-cli/templates/backend/.prettierrc +0 -6
- package/frigg-cli/templates/backend/docker-compose.yml +0 -22
- package/frigg-cli/templates/backend/index.js +0 -96
- package/frigg-cli/templates/backend/infrastructure.js +0 -12
- package/frigg-cli/templates/backend/jest.config.js +0 -17
- package/frigg-cli/templates/backend/package.json +0 -50
- package/frigg-cli/templates/backend/src/api-modules/.gitkeep +0 -10
- package/frigg-cli/templates/backend/src/base/.gitkeep +0 -7
- package/frigg-cli/templates/backend/src/integrations/.gitkeep +0 -10
- package/frigg-cli/templates/backend/src/integrations/ExampleIntegration.js +0 -65
- package/frigg-cli/templates/backend/src/utils/.gitkeep +0 -7
- package/frigg-cli/templates/backend/test/setup.js +0 -30
- package/frigg-cli/templates/backend/ui-extensions/.gitkeep +0 -0
- package/frigg-cli/templates/backend/ui-extensions/README.md +0 -77
- package/frigg-cli/utils/__tests__/provider-helper.test.js +0 -55
- package/frigg-cli/utils/__tests__/repo-detection.test.js +0 -436
- package/frigg-cli/utils/output.js +0 -382
- package/frigg-cli/utils/provider-helper.js +0 -75
- package/frigg-cli/validate-command/__tests__/adapters/validate-command.test.js +0 -205
- package/frigg-cli/validate-command/__tests__/application/validate-app-use-case.test.js +0 -104
- package/frigg-cli/validate-command/__tests__/domain/fix-suggestion.test.js +0 -153
- package/frigg-cli/validate-command/__tests__/domain/validation-error.test.js +0 -162
- package/frigg-cli/validate-command/__tests__/domain/validation-result.test.js +0 -152
- package/frigg-cli/validate-command/__tests__/infrastructure/api-module-validator.test.js +0 -332
- package/frigg-cli/validate-command/__tests__/infrastructure/app-definition-validator.test.js +0 -191
- package/frigg-cli/validate-command/__tests__/infrastructure/integration-class-validator.test.js +0 -146
- package/frigg-cli/validate-command/__tests__/infrastructure/template-validation.test.js +0 -155
- package/frigg-cli/validate-command/adapters/cli/validate-command.js +0 -199
- package/frigg-cli/validate-command/application/use-cases/validate-app-use-case.js +0 -35
- package/frigg-cli/validate-command/domain/entities/validation-result.js +0 -74
- package/frigg-cli/validate-command/domain/value-objects/fix-suggestion.js +0 -74
- package/frigg-cli/validate-command/domain/value-objects/validation-error.js +0 -68
- package/frigg-cli/validate-command/infrastructure/validators/api-module-validator.js +0 -181
- package/frigg-cli/validate-command/infrastructure/validators/app-definition-validator.js +0 -145
- package/frigg-cli/validate-command/infrastructure/validators/integration-class-validator.js +0 -113
- package/infrastructure/domains/admin-scripts/admin-script-builder.js +0 -200
- package/infrastructure/domains/admin-scripts/admin-script-builder.test.js +0 -499
- package/infrastructure/domains/admin-scripts/index.js +0 -5
- 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};
|