@tamyla/clodo-framework 1.0.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 (130) hide show
  1. package/CHANGELOG.md +564 -0
  2. package/LICENSE +21 -0
  3. package/README.md +1393 -0
  4. package/bin/README.md +71 -0
  5. package/bin/clodo-service.js +416 -0
  6. package/bin/security/security-cli.js +96 -0
  7. package/bin/service-management/README.md +74 -0
  8. package/bin/service-management/create-service.js +129 -0
  9. package/bin/service-management/init-service.js +102 -0
  10. package/bin/service-management/init-service.js.backup +889 -0
  11. package/bin/shared/config/customer-cli.js +293 -0
  12. package/dist/config/ConfigurationManager.js +159 -0
  13. package/dist/config/CustomerConfigCLI.js +220 -0
  14. package/dist/config/FeatureManager.js +426 -0
  15. package/dist/config/customers.js +441 -0
  16. package/dist/config/domains.js +180 -0
  17. package/dist/config/features.js +225 -0
  18. package/dist/config/index.js +6 -0
  19. package/dist/database/database-orchestrator.js +730 -0
  20. package/dist/database/index.js +4 -0
  21. package/dist/deployment/auditor.js +971 -0
  22. package/dist/deployment/index.js +10 -0
  23. package/dist/deployment/rollback-manager.js +523 -0
  24. package/dist/deployment/testers/api-tester.js +80 -0
  25. package/dist/deployment/testers/auth-tester.js +129 -0
  26. package/dist/deployment/testers/core.js +217 -0
  27. package/dist/deployment/testers/database-tester.js +105 -0
  28. package/dist/deployment/testers/index.js +74 -0
  29. package/dist/deployment/testers/load-tester.js +120 -0
  30. package/dist/deployment/testers/performance-tester.js +105 -0
  31. package/dist/deployment/validator.js +558 -0
  32. package/dist/deployment/wrangler-deployer.js +574 -0
  33. package/dist/handlers/GenericRouteHandler.js +532 -0
  34. package/dist/index.js +39 -0
  35. package/dist/migration/MigrationAdapters.js +562 -0
  36. package/dist/modules/ModuleManager.js +668 -0
  37. package/dist/modules/security.js +98 -0
  38. package/dist/orchestration/cross-domain-coordinator.js +1083 -0
  39. package/dist/orchestration/index.js +5 -0
  40. package/dist/orchestration/modules/DeploymentCoordinator.js +258 -0
  41. package/dist/orchestration/modules/DomainResolver.js +196 -0
  42. package/dist/orchestration/modules/StateManager.js +332 -0
  43. package/dist/orchestration/multi-domain-orchestrator.js +255 -0
  44. package/dist/routing/EnhancedRouter.js +158 -0
  45. package/dist/schema/SchemaManager.js +778 -0
  46. package/dist/security/ConfigurationValidator.js +490 -0
  47. package/dist/security/DeploymentManager.js +208 -0
  48. package/dist/security/SecretGenerator.js +142 -0
  49. package/dist/security/SecurityCLI.js +228 -0
  50. package/dist/security/index.js +51 -0
  51. package/dist/security/patterns/environment-rules.js +66 -0
  52. package/dist/security/patterns/insecure-patterns.js +21 -0
  53. package/dist/service-management/ConfirmationEngine.js +411 -0
  54. package/dist/service-management/ErrorTracker.js +294 -0
  55. package/dist/service-management/GenerationEngine.js +3109 -0
  56. package/dist/service-management/InputCollector.js +237 -0
  57. package/dist/service-management/ServiceCreator.js +229 -0
  58. package/dist/service-management/ServiceInitializer.js +448 -0
  59. package/dist/service-management/ServiceOrchestrator.js +638 -0
  60. package/dist/service-management/handlers/ConfigMutator.js +130 -0
  61. package/dist/service-management/handlers/ConfirmationHandler.js +71 -0
  62. package/dist/service-management/handlers/GenerationHandler.js +80 -0
  63. package/dist/service-management/handlers/InputHandler.js +59 -0
  64. package/dist/service-management/handlers/ValidationHandler.js +203 -0
  65. package/dist/service-management/index.js +7 -0
  66. package/dist/services/GenericDataService.js +488 -0
  67. package/dist/shared/cloudflare/domain-discovery.js +562 -0
  68. package/dist/shared/cloudflare/domain-manager.js +912 -0
  69. package/dist/shared/cloudflare/index.js +8 -0
  70. package/dist/shared/cloudflare/ops.js +387 -0
  71. package/dist/shared/config/cache.js +1167 -0
  72. package/dist/shared/config/command-config-manager.js +174 -0
  73. package/dist/shared/config/customer-cli.js +258 -0
  74. package/dist/shared/config/index.js +9 -0
  75. package/dist/shared/config/manager.js +289 -0
  76. package/dist/shared/database/connection-manager.js +338 -0
  77. package/dist/shared/database/index.js +7 -0
  78. package/dist/shared/database/orchestrator.js +632 -0
  79. package/dist/shared/deployment/auditor.js +971 -0
  80. package/dist/shared/deployment/index.js +10 -0
  81. package/dist/shared/deployment/rollback-manager.js +523 -0
  82. package/dist/shared/deployment/validator.js +558 -0
  83. package/dist/shared/index.js +32 -0
  84. package/dist/shared/monitoring/health-checker.js +250 -0
  85. package/dist/shared/monitoring/index.js +8 -0
  86. package/dist/shared/monitoring/memory-manager.js +382 -0
  87. package/dist/shared/monitoring/production-monitor.js +390 -0
  88. package/dist/shared/production-tester/api-tester.js +80 -0
  89. package/dist/shared/production-tester/auth-tester.js +129 -0
  90. package/dist/shared/production-tester/core.js +217 -0
  91. package/dist/shared/production-tester/database-tester.js +105 -0
  92. package/dist/shared/production-tester/index.js +74 -0
  93. package/dist/shared/production-tester/load-tester.js +120 -0
  94. package/dist/shared/production-tester/performance-tester.js +105 -0
  95. package/dist/shared/security/api-token-manager.js +296 -0
  96. package/dist/shared/security/index.js +8 -0
  97. package/dist/shared/security/secret-generator.js +918 -0
  98. package/dist/shared/security/secure-token-manager.js +379 -0
  99. package/dist/shared/utils/error-recovery.js +240 -0
  100. package/dist/shared/utils/graceful-shutdown-manager.js +380 -0
  101. package/dist/shared/utils/index.js +9 -0
  102. package/dist/shared/utils/interactive-prompts.js +134 -0
  103. package/dist/shared/utils/rate-limiter.js +249 -0
  104. package/dist/utils/ErrorHandler.js +173 -0
  105. package/dist/utils/deployment/config-cache.js +1160 -0
  106. package/dist/utils/deployment/index.js +6 -0
  107. package/dist/utils/deployment/interactive-prompts.js +97 -0
  108. package/dist/utils/deployment/secret-generator.js +896 -0
  109. package/dist/utils/dirname-helper.js +35 -0
  110. package/dist/utils/domain-config.js +159 -0
  111. package/dist/utils/error-recovery.js +240 -0
  112. package/dist/utils/esm-helper.js +52 -0
  113. package/dist/utils/framework-config.js +481 -0
  114. package/dist/utils/graceful-shutdown-manager.js +379 -0
  115. package/dist/utils/health-checker.js +114 -0
  116. package/dist/utils/index.js +36 -0
  117. package/dist/utils/prompt-handler.js +98 -0
  118. package/dist/utils/usage-tracker.js +252 -0
  119. package/dist/utils/validation.js +112 -0
  120. package/dist/version/VersionDetector.js +723 -0
  121. package/dist/worker/index.js +4 -0
  122. package/dist/worker/integration.js +332 -0
  123. package/docs/FRAMEWORK-ARCHITECTURE-OVERVIEW.md +206 -0
  124. package/docs/INTEGRATION_GUIDE.md +2045 -0
  125. package/docs/README.md +82 -0
  126. package/docs/SECURITY.md +242 -0
  127. package/docs/deployment/deployment-guide.md +540 -0
  128. package/docs/overview.md +280 -0
  129. package/package.json +176 -0
  130. package/types/index.d.ts +575 -0
@@ -0,0 +1,237 @@
1
+ /**
2
+ * InputCollector - Tier 1: Core Input Collection
3
+ *
4
+ * Collects the 6 absolutely required inputs for service creation:
5
+ * 1. Service Name
6
+ * 2. Service Type
7
+ * 3. Domain Name
8
+ * 4. Cloudflare API Token
9
+ * 5. Cloudflare Account ID
10
+ * 6. Cloudflare Zone ID
11
+ * 7. Target Environment (derived but required)
12
+ */
13
+
14
+ import { createInterface } from 'readline';
15
+ import chalk from 'chalk';
16
+ import { validateServiceName, validateDomainName } from '../utils/validation.js';
17
+ export class InputCollector {
18
+ constructor(options = {}) {
19
+ this.interactive = options.interactive !== false;
20
+ this.rl = this.interactive ? createInterface({
21
+ input: process.stdin,
22
+ output: process.stdout
23
+ }) : null;
24
+ }
25
+
26
+ /**
27
+ * Collect all 6 core inputs interactively
28
+ */
29
+ async collectCoreInputs() {
30
+ const inputs = {};
31
+ console.log(chalk.cyan('📝 Collecting Core Service Information'));
32
+ console.log(chalk.white('These 6 inputs are required to create your Clodo service.\n'));
33
+
34
+ // 1. Service Name
35
+ inputs.serviceName = await this.collectServiceName();
36
+
37
+ // 2. Service Type
38
+ inputs.serviceType = await this.collectServiceType();
39
+
40
+ // 3. Domain Name
41
+ inputs.domainName = await this.collectDomainName();
42
+
43
+ // 4. Cloudflare API Token
44
+ inputs.cloudflareToken = await this.collectCloudflareToken();
45
+
46
+ // 5. Cloudflare Account ID
47
+ inputs.cloudflareAccountId = await this.collectCloudflareAccountId();
48
+
49
+ // 6. Cloudflare Zone ID
50
+ inputs.cloudflareZoneId = await this.collectCloudflareZoneId();
51
+
52
+ // 7. Target Environment
53
+ inputs.environment = await this.collectEnvironment();
54
+ return inputs;
55
+ }
56
+
57
+ /**
58
+ * Validate core inputs (for non-interactive mode)
59
+ */
60
+ async validateCoreInputs(inputs) {
61
+ const required = ['serviceName', 'serviceType', 'domainName', 'cloudflareToken', 'cloudflareAccountId', 'cloudflareZoneId', 'environment'];
62
+ for (const field of required) {
63
+ if (!inputs[field]) {
64
+ throw new Error(`Missing required input: ${field}`);
65
+ }
66
+ }
67
+
68
+ // Validate service name
69
+ if (!validateServiceName(inputs.serviceName)) {
70
+ throw new Error('Invalid service name format');
71
+ }
72
+
73
+ // Validate domain name
74
+ if (!validateDomainName(inputs.domainName)) {
75
+ throw new Error('Invalid domain name format');
76
+ }
77
+
78
+ // Validate service type
79
+ const validTypes = ['data-service', 'auth-service', 'content-service', 'api-gateway', 'generic'];
80
+ if (!validTypes.includes(inputs.serviceType)) {
81
+ throw new Error(`Invalid service type. Must be one of: ${validTypes.join(', ')}`);
82
+ }
83
+
84
+ // Validate environment
85
+ const validEnvs = ['development', 'staging', 'production'];
86
+ if (!validEnvs.includes(inputs.environment)) {
87
+ throw new Error(`Invalid environment. Must be one of: ${validEnvs.join(', ')}`);
88
+ }
89
+ }
90
+
91
+ /**
92
+ * Collect service name with validation
93
+ */
94
+ async collectServiceName() {
95
+ for (;;) {
96
+ const name = await this.prompt('Service Name (lowercase, letters/numbers/hyphens only): ');
97
+ if (validateServiceName(name)) {
98
+ return name;
99
+ }
100
+ console.log(chalk.red('✗ Invalid service name. Use only lowercase letters, numbers, and hyphens.'));
101
+ }
102
+ }
103
+
104
+ /**
105
+ * Collect service type with menu
106
+ */
107
+ async collectServiceType() {
108
+ const types = {
109
+ 'data-service': 'Data service with CRUD operations, search, and filtering',
110
+ 'auth-service': 'Authentication and authorization service',
111
+ 'content-service': 'Content management with file storage and search',
112
+ 'api-gateway': 'API gateway with rate limiting and routing',
113
+ 'generic': 'Basic service with core Clodo Framework features'
114
+ };
115
+ console.log(chalk.cyan('Available Service Types:'));
116
+ Object.entries(types).forEach(([type, desc]) => {
117
+ console.log(chalk.white(` ${type}: ${desc}`));
118
+ });
119
+ console.log('');
120
+ for (;;) {
121
+ const type = await this.prompt('Service Type (default: generic): ', 'generic');
122
+ if (Object.keys(types).includes(type)) {
123
+ return type;
124
+ }
125
+ console.log(chalk.red('✗ Invalid service type. Choose from the list above.'));
126
+ }
127
+ }
128
+
129
+ /**
130
+ * Collect domain name with validation
131
+ */
132
+ async collectDomainName() {
133
+ for (;;) {
134
+ const domain = await this.prompt('Domain Name (e.g., my-service.com): ');
135
+ if (validateDomainName(domain)) {
136
+ return domain;
137
+ }
138
+ console.log(chalk.red('✗ Invalid domain name format.'));
139
+ }
140
+ }
141
+
142
+ /**
143
+ * Collect Cloudflare API token
144
+ */
145
+ async collectCloudflareToken() {
146
+ console.log(chalk.yellow('Cloudflare Configuration:'));
147
+ console.log(chalk.white('You can find your API token at: https://dash.cloudflare.com/profile/api-tokens'));
148
+ console.log('');
149
+ for (;;) {
150
+ const token = await this.prompt('Cloudflare API Token: ');
151
+ if (token && token.length > 20) {
152
+ // Basic length validation
153
+ return token;
154
+ }
155
+ console.log(chalk.red('✗ Invalid API token format.'));
156
+ }
157
+ }
158
+
159
+ /**
160
+ * Collect Cloudflare Account ID
161
+ */
162
+ async collectCloudflareAccountId() {
163
+ console.log(chalk.white('Find your Account ID in the right sidebar of your Cloudflare dashboard.'));
164
+ console.log('');
165
+ for (;;) {
166
+ const accountId = await this.prompt('Cloudflare Account ID: ');
167
+ if (accountId && /^[a-f0-9]{32}$/i.test(accountId)) {
168
+ // 32 hex chars
169
+ return accountId;
170
+ }
171
+ console.log(chalk.red('✗ Invalid Account ID format (should be 32 hexadecimal characters).'));
172
+ }
173
+ }
174
+
175
+ /**
176
+ * Collect Cloudflare Zone ID
177
+ */
178
+ async collectCloudflareZoneId() {
179
+ console.log(chalk.white('Find your Zone ID in the Overview tab of your domain in Cloudflare.'));
180
+ console.log('');
181
+ for (;;) {
182
+ const zoneId = await this.prompt('Cloudflare Zone ID: ');
183
+ if (zoneId && /^[a-f0-9]{32}$/i.test(zoneId)) {
184
+ // 32 hex chars
185
+ return zoneId;
186
+ }
187
+ console.log(chalk.red('✗ Invalid Zone ID format (should be 32 hexadecimal characters).'));
188
+ }
189
+ }
190
+
191
+ /**
192
+ * Collect target environment
193
+ */
194
+ async collectEnvironment() {
195
+ const environments = {
196
+ 'development': 'Local development environment',
197
+ 'staging': 'Staging environment for testing',
198
+ 'production': 'Production environment for live services'
199
+ };
200
+ console.log(chalk.cyan('Target Environment:'));
201
+ Object.entries(environments).forEach(([env, desc]) => {
202
+ console.log(chalk.white(` ${env}: ${desc}`));
203
+ });
204
+ console.log('');
205
+ for (;;) {
206
+ const env = await this.prompt('Environment (default: development): ', 'development');
207
+ if (Object.keys(environments).includes(env)) {
208
+ return env;
209
+ }
210
+ console.log(chalk.red('✗ Invalid environment. Choose from development, staging, or production.'));
211
+ }
212
+ }
213
+
214
+ /**
215
+ * Prompt user for input (interactive mode)
216
+ */
217
+ async prompt(question, defaultValue = '') {
218
+ if (!this.interactive) {
219
+ throw new Error('Cannot prompt in non-interactive mode');
220
+ }
221
+ return new Promise(resolve => {
222
+ const promptText = defaultValue ? `${question}(default: ${defaultValue}) ` : question;
223
+ this.rl.question(promptText, answer => {
224
+ resolve(answer || defaultValue);
225
+ });
226
+ });
227
+ }
228
+
229
+ /**
230
+ * Close readline interface
231
+ */
232
+ close() {
233
+ if (this.rl) {
234
+ this.rl.close();
235
+ }
236
+ }
237
+ }
@@ -0,0 +1,229 @@
1
+ /**
2
+ * Clodo Framework - Service Creator
3
+ * Programmatic API for creating services from templates
4
+ */
5
+
6
+ import { readFileSync, writeFileSync, mkdirSync, existsSync, cpSync, readdirSync, statSync } from 'fs';
7
+ import { join, dirname, resolve } from 'path';
8
+ import { fileURLToPath } from 'url';
9
+ const __filename = fileURLToPath(import.meta.url);
10
+ const __dirname = dirname(__filename);
11
+ const TEMPLATES_DIR = join(__dirname, '..', '..', 'templates');
12
+ const SERVICE_TYPES = ['data-service', 'auth-service', 'content-service', 'api-gateway', 'generic'];
13
+ export class ServiceCreator {
14
+ constructor(options = {}) {
15
+ this.templatesDir = options.templatesDir || TEMPLATES_DIR;
16
+ this.serviceTypes = options.serviceTypes || SERVICE_TYPES;
17
+ }
18
+
19
+ /**
20
+ * Validate service name according to framework conventions
21
+ * @param {string} name - Service name to validate
22
+ * @throws {Error} If validation fails
23
+ */
24
+ validateServiceName(name) {
25
+ if (!/^[a-z0-9-]+$/.test(name)) {
26
+ throw new Error('Service name must contain only lowercase letters, numbers, and hyphens');
27
+ }
28
+ if (name.length < 3) {
29
+ throw new Error('Service name must be at least 3 characters long');
30
+ }
31
+ if (name.length > 50) {
32
+ throw new Error('Service name must be no more than 50 characters long');
33
+ }
34
+ }
35
+
36
+ /**
37
+ * Validate service type
38
+ * @param {string} type - Service type to validate
39
+ * @throws {Error} If type is invalid
40
+ */
41
+ validateServiceType(type) {
42
+ if (!this.serviceTypes.includes(type)) {
43
+ throw new Error(`Invalid service type. Must be one of: ${this.serviceTypes.join(', ')}`);
44
+ }
45
+ }
46
+
47
+ /**
48
+ * Create a service from a template
49
+ * @param {string} serviceName - Name of the service to create
50
+ * @param {Object} options - Creation options
51
+ * @param {string} options.type - Service type (default: 'generic')
52
+ * @param {string} options.output - Output directory (default: current directory)
53
+ * @param {boolean} options.force - Overwrite existing directory (default: false)
54
+ * @param {Object} options.variables - Additional template variables
55
+ * @returns {Object} Creation result with success status and metadata
56
+ */
57
+ async createService(serviceName, options = {}) {
58
+ const config = {
59
+ type: 'generic',
60
+ output: process.cwd(),
61
+ force: false,
62
+ variables: {},
63
+ ...options
64
+ };
65
+ try {
66
+ // Validate inputs
67
+ this.validateServiceName(serviceName);
68
+ this.validateServiceType(config.type);
69
+ const templateDir = join(this.templatesDir, config.type);
70
+ const serviceDir = join(config.output, serviceName);
71
+
72
+ // Check if template exists
73
+ if (!existsSync(templateDir)) {
74
+ throw new Error(`Template not found: ${templateDir}. Available templates: ${this.serviceTypes.join(', ')}`);
75
+ }
76
+
77
+ // Check if service directory already exists
78
+ if (existsSync(serviceDir) && !config.force) {
79
+ throw new Error(`Service directory already exists: ${serviceDir}. Use force option to overwrite.`);
80
+ }
81
+
82
+ // Copy template to service directory
83
+ cpSync(templateDir, serviceDir, {
84
+ recursive: true
85
+ });
86
+
87
+ // Prepare template variables
88
+ const defaultVariables = {
89
+ '{{SERVICE_NAME}}': serviceName,
90
+ '{{SERVICE_TYPE}}': config.type,
91
+ '{{SERVICE_DISPLAY_NAME}}': this.toTitleCase(serviceName.replace(/-/g, ' ')),
92
+ '{{CURRENT_DATE}}': new Date().toISOString().split('T')[0],
93
+ '{{CURRENT_YEAR}}': new Date().getFullYear().toString(),
94
+ '{{FRAMEWORK_VERSION}}': this.getFrameworkVersion()
95
+ };
96
+ const allVariables = {
97
+ ...defaultVariables,
98
+ ...config.variables
99
+ };
100
+
101
+ // Replace template variables
102
+ this.replaceTemplateVariables(serviceDir, allVariables);
103
+ return {
104
+ success: true,
105
+ serviceName,
106
+ serviceType: config.type,
107
+ serviceDir,
108
+ templateDir,
109
+ variables: allVariables
110
+ };
111
+ } catch (error) {
112
+ return {
113
+ success: false,
114
+ serviceName,
115
+ error: error.message
116
+ };
117
+ }
118
+ }
119
+
120
+ /**
121
+ * Replace template variables in all files within a directory
122
+ * @param {string} dir - Directory to process
123
+ * @param {Object} variables - Variable mappings
124
+ */
125
+ replaceTemplateVariables(dir, variables) {
126
+ const files = this.getAllFiles(dir);
127
+ for (const file of files) {
128
+ try {
129
+ let content = readFileSync(file, 'utf8');
130
+ let modified = false;
131
+ for (const [placeholder, value] of Object.entries(variables)) {
132
+ if (content.includes(placeholder)) {
133
+ content = content.replace(new RegExp(this.escapeRegExp(placeholder), 'g'), value);
134
+ modified = true;
135
+ }
136
+ }
137
+ if (modified) {
138
+ writeFileSync(file, content, 'utf8');
139
+ }
140
+ } catch (error) {
141
+ // Skip binary files or files that can't be read
142
+ if (error.code !== 'EISDIR') {
143
+ // In programmatic usage, we might want to collect warnings
144
+ // For now, we'll silently skip problematic files
145
+ }
146
+ }
147
+ }
148
+ }
149
+
150
+ /**
151
+ * Get all files in a directory recursively
152
+ * @param {string} dir - Directory to traverse
153
+ * @returns {string[]} Array of file paths
154
+ */
155
+ getAllFiles(dir) {
156
+ const files = [];
157
+ function traverse(currentDir) {
158
+ const items = readdirSync(currentDir);
159
+ for (const item of items) {
160
+ const fullPath = join(currentDir, item);
161
+ const stat = statSync(fullPath);
162
+ if (stat.isDirectory()) {
163
+ // Skip node_modules and other unwanted directories
164
+ if (!['node_modules', '.git', 'dist', 'build'].includes(item)) {
165
+ traverse(fullPath);
166
+ }
167
+ } else {
168
+ files.push(fullPath);
169
+ }
170
+ }
171
+ }
172
+ traverse(dir);
173
+ return files;
174
+ }
175
+
176
+ /**
177
+ * Escape special regex characters
178
+ * @param {string} string - String to escape
179
+ * @returns {string} Escaped string
180
+ */
181
+ escapeRegExp(string) {
182
+ return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
183
+ }
184
+
185
+ /**
186
+ * Convert string to title case
187
+ * @param {string} str - String to convert
188
+ * @returns {string} Title case string
189
+ */
190
+ toTitleCase(str) {
191
+ return str.split(' ').map(word => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()).join(' ');
192
+ }
193
+
194
+ /**
195
+ * Get framework version from package.json
196
+ * @returns {string} Framework version
197
+ */
198
+ getFrameworkVersion() {
199
+ try {
200
+ const packageJson = JSON.parse(readFileSync(join(__dirname, '..', '..', 'package.json'), 'utf8'));
201
+ return packageJson.version;
202
+ } catch {
203
+ return '1.0.0';
204
+ }
205
+ }
206
+
207
+ /**
208
+ * Get available service types
209
+ * @returns {string[]} Array of available service types
210
+ */
211
+ getServiceTypes() {
212
+ return [...this.serviceTypes];
213
+ }
214
+
215
+ /**
216
+ * Check if a service type is available
217
+ * @param {string} type - Service type to check
218
+ * @returns {boolean} True if type is available
219
+ */
220
+ isServiceTypeAvailable(type) {
221
+ return this.serviceTypes.includes(type);
222
+ }
223
+ }
224
+
225
+ // Convenience function for quick service creation
226
+ export async function createService(serviceName, options = {}) {
227
+ const creator = new ServiceCreator();
228
+ return await creator.createService(serviceName, options);
229
+ }