@edgible-team/cli 1.0.1

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 (102) hide show
  1. package/LICENSE +136 -0
  2. package/README.md +450 -0
  3. package/dist/client/api-client.js +1057 -0
  4. package/dist/client/index.js +21 -0
  5. package/dist/commands/agent.js +1280 -0
  6. package/dist/commands/ai.js +608 -0
  7. package/dist/commands/application.js +885 -0
  8. package/dist/commands/auth.js +570 -0
  9. package/dist/commands/base/BaseCommand.js +93 -0
  10. package/dist/commands/base/CommandHandler.js +7 -0
  11. package/dist/commands/base/command-wrapper.js +58 -0
  12. package/dist/commands/base/middleware.js +77 -0
  13. package/dist/commands/config.js +116 -0
  14. package/dist/commands/connectivity.js +59 -0
  15. package/dist/commands/debug.js +98 -0
  16. package/dist/commands/discover.js +144 -0
  17. package/dist/commands/examples/migrated-command-example.js +180 -0
  18. package/dist/commands/gateway.js +494 -0
  19. package/dist/commands/managedGateway.js +787 -0
  20. package/dist/commands/utils/config-validator.js +76 -0
  21. package/dist/commands/utils/gateway-prompt.js +79 -0
  22. package/dist/commands/utils/input-parser.js +120 -0
  23. package/dist/commands/utils/output-formatter.js +109 -0
  24. package/dist/config/app-config.js +99 -0
  25. package/dist/detection/SystemCapabilityDetector.js +1244 -0
  26. package/dist/detection/ToolDetector.js +305 -0
  27. package/dist/detection/WorkloadDetector.js +314 -0
  28. package/dist/di/bindings.js +99 -0
  29. package/dist/di/container.js +88 -0
  30. package/dist/di/types.js +32 -0
  31. package/dist/index.js +52 -0
  32. package/dist/interfaces/IDaemonManager.js +3 -0
  33. package/dist/repositories/config-repository.js +62 -0
  34. package/dist/repositories/gateway-repository.js +35 -0
  35. package/dist/scripts/postinstall.js +101 -0
  36. package/dist/services/AgentStatusManager.js +299 -0
  37. package/dist/services/ConnectivityTester.js +271 -0
  38. package/dist/services/DependencyInstaller.js +475 -0
  39. package/dist/services/LocalAgentManager.js +2216 -0
  40. package/dist/services/application/ApplicationService.js +299 -0
  41. package/dist/services/auth/AuthService.js +214 -0
  42. package/dist/services/aws.js +644 -0
  43. package/dist/services/daemon/DaemonManagerFactory.js +65 -0
  44. package/dist/services/daemon/DockerDaemonManager.js +395 -0
  45. package/dist/services/daemon/LaunchdDaemonManager.js +257 -0
  46. package/dist/services/daemon/PodmanDaemonManager.js +369 -0
  47. package/dist/services/daemon/SystemdDaemonManager.js +221 -0
  48. package/dist/services/daemon/WindowsServiceDaemonManager.js +210 -0
  49. package/dist/services/daemon/index.js +16 -0
  50. package/dist/services/edgible.js +3060 -0
  51. package/dist/services/gateway/GatewayService.js +334 -0
  52. package/dist/state/config.js +146 -0
  53. package/dist/types/AgentConfig.js +5 -0
  54. package/dist/types/AgentStatus.js +5 -0
  55. package/dist/types/ApiClient.js +5 -0
  56. package/dist/types/ApiRequests.js +5 -0
  57. package/dist/types/ApiResponses.js +5 -0
  58. package/dist/types/Application.js +5 -0
  59. package/dist/types/CaddyJson.js +5 -0
  60. package/dist/types/UnifiedAgentStatus.js +56 -0
  61. package/dist/types/WireGuard.js +5 -0
  62. package/dist/types/Workload.js +5 -0
  63. package/dist/types/agent.js +5 -0
  64. package/dist/types/command-options.js +5 -0
  65. package/dist/types/connectivity.js +5 -0
  66. package/dist/types/errors.js +250 -0
  67. package/dist/types/gateway-types.js +5 -0
  68. package/dist/types/index.js +48 -0
  69. package/dist/types/models/ApplicationData.js +5 -0
  70. package/dist/types/models/CertificateData.js +5 -0
  71. package/dist/types/models/DeviceData.js +5 -0
  72. package/dist/types/models/DevicePoolData.js +5 -0
  73. package/dist/types/models/OrganizationData.js +5 -0
  74. package/dist/types/models/OrganizationInviteData.js +5 -0
  75. package/dist/types/models/ProviderConfiguration.js +5 -0
  76. package/dist/types/models/ResourceData.js +5 -0
  77. package/dist/types/models/ServiceResourceData.js +5 -0
  78. package/dist/types/models/UserData.js +5 -0
  79. package/dist/types/route.js +5 -0
  80. package/dist/types/validation/schemas.js +218 -0
  81. package/dist/types/validation.js +5 -0
  82. package/dist/utils/FileIntegrityManager.js +256 -0
  83. package/dist/utils/PathMigration.js +219 -0
  84. package/dist/utils/PathResolver.js +235 -0
  85. package/dist/utils/PlatformDetector.js +277 -0
  86. package/dist/utils/console-logger.js +130 -0
  87. package/dist/utils/docker-compose-parser.js +179 -0
  88. package/dist/utils/errors.js +130 -0
  89. package/dist/utils/health-checker.js +155 -0
  90. package/dist/utils/json-logger.js +72 -0
  91. package/dist/utils/log-formatter.js +293 -0
  92. package/dist/utils/logger.js +59 -0
  93. package/dist/utils/network-utils.js +217 -0
  94. package/dist/utils/output.js +182 -0
  95. package/dist/utils/passwordValidation.js +91 -0
  96. package/dist/utils/progress.js +167 -0
  97. package/dist/utils/sudo-checker.js +22 -0
  98. package/dist/utils/urls.js +32 -0
  99. package/dist/utils/validation.js +31 -0
  100. package/dist/validation/schemas.js +175 -0
  101. package/dist/validation/validator.js +67 -0
  102. package/package.json +83 -0
@@ -0,0 +1,76 @@
1
+ "use strict";
2
+ /**
3
+ * Configuration validation utilities
4
+ * Centralizes config validation logic
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.validateConfig = validateConfig;
8
+ exports.requireOrganizationId = requireOrganizationId;
9
+ exports.requireDeviceId = requireDeviceId;
10
+ exports.requireEmail = requireEmail;
11
+ const errors_1 = require("../../utils/errors");
12
+ /**
13
+ * Validate configuration based on requirements
14
+ */
15
+ function validateConfig(configRepository, options = {}) {
16
+ const { requireAuth = false, requireOrganization = false, requireDeviceId = false, requireEmail = false, requireAwsProfile = false, } = options;
17
+ const config = configRepository.getConfig();
18
+ if (requireAuth) {
19
+ const hasTokens = !!(config.accessToken || config.idToken);
20
+ const hasDeviceCreds = !!(config.deviceId && config.devicePassword);
21
+ if (!hasTokens && !hasDeviceCreds) {
22
+ throw new errors_1.ConfigError('Not authenticated. Please run "edgible login" first.');
23
+ }
24
+ }
25
+ if (requireOrganization) {
26
+ if (!config.organizationId) {
27
+ throw new errors_1.ConfigError('No organization found. Please login first.');
28
+ }
29
+ }
30
+ if (requireDeviceId) {
31
+ if (!config.deviceId) {
32
+ throw new errors_1.ConfigError('No device ID found. Please login first.');
33
+ }
34
+ }
35
+ if (requireEmail) {
36
+ if (!config.email) {
37
+ throw new errors_1.ConfigError('No email found in configuration.');
38
+ }
39
+ }
40
+ if (requireAwsProfile) {
41
+ if (!config.awsProfile) {
42
+ throw new errors_1.ConfigError('No AWS profile configured. Please set AWS profile first.');
43
+ }
44
+ }
45
+ }
46
+ /**
47
+ * Get organization ID or throw error
48
+ */
49
+ function requireOrganizationId(configRepository) {
50
+ const config = configRepository.getConfig();
51
+ if (!config.organizationId) {
52
+ throw new errors_1.ConfigError('No organization ID found. Please login first.');
53
+ }
54
+ return config.organizationId;
55
+ }
56
+ /**
57
+ * Get device ID or throw error
58
+ */
59
+ function requireDeviceId(configRepository) {
60
+ const config = configRepository.getConfig();
61
+ if (!config.deviceId) {
62
+ throw new errors_1.ConfigError('No device ID found. Please login first.');
63
+ }
64
+ return config.deviceId;
65
+ }
66
+ /**
67
+ * Get email or throw error
68
+ */
69
+ function requireEmail(configRepository) {
70
+ const config = configRepository.getConfig();
71
+ if (!config.email) {
72
+ throw new errors_1.ConfigError('No email found in configuration.');
73
+ }
74
+ return config.email;
75
+ }
76
+ //# sourceMappingURL=config-validator.js.map
@@ -0,0 +1,79 @@
1
+ "use strict";
2
+ /**
3
+ * Shared gateway selection utilities
4
+ * Eliminates duplication across commands
5
+ */
6
+ var __importDefault = (this && this.__importDefault) || function (mod) {
7
+ return (mod && mod.__esModule) ? mod : { "default": mod };
8
+ };
9
+ Object.defineProperty(exports, "__esModule", { value: true });
10
+ exports.promptGatewaySelection = promptGatewaySelection;
11
+ exports.getGatewayId = getGatewayId;
12
+ exports.validateGatewayExists = validateGatewayExists;
13
+ const inquirer_1 = __importDefault(require("inquirer"));
14
+ const chalk_1 = __importDefault(require("chalk"));
15
+ /**
16
+ * Prompt user to select a gateway from available gateways
17
+ */
18
+ async function promptGatewaySelection(gatewayService, options = {}) {
19
+ const { allowNone = false, message = 'Select gateway:', required = true } = options;
20
+ try {
21
+ const response = await gatewayService.listGateways();
22
+ if (response.gateways.length === 0) {
23
+ if (allowNone) {
24
+ return null;
25
+ }
26
+ console.log(chalk_1.default.yellow('⚠ No gateways found'));
27
+ console.log(chalk_1.default.gray('Use "edgible gateway create" to create a gateway'));
28
+ if (required) {
29
+ throw new Error('No gateways available');
30
+ }
31
+ return null;
32
+ }
33
+ const gatewayChoices = response.gateways.map((gateway) => ({
34
+ name: `${gateway.device.name} (${gateway.device.id})`,
35
+ value: gateway.device.id,
36
+ }));
37
+ if (allowNone) {
38
+ gatewayChoices.push({
39
+ name: 'None',
40
+ value: '',
41
+ });
42
+ }
43
+ const answer = await inquirer_1.default.prompt([
44
+ {
45
+ type: 'list',
46
+ name: 'gatewayId',
47
+ message,
48
+ choices: gatewayChoices,
49
+ },
50
+ ]);
51
+ return answer.gatewayId === '' ? null : answer.gatewayId;
52
+ }
53
+ catch (error) {
54
+ console.error(chalk_1.default.red('Error listing gateways:'), error instanceof Error ? error.message : 'Unknown error');
55
+ throw error;
56
+ }
57
+ }
58
+ /**
59
+ * Get gateway by ID, or prompt if not provided
60
+ */
61
+ async function getGatewayId(gatewayService, providedId, options = {}) {
62
+ if (providedId) {
63
+ return providedId;
64
+ }
65
+ return await promptGatewaySelection(gatewayService, options);
66
+ }
67
+ /**
68
+ * Validate gateway exists
69
+ */
70
+ async function validateGatewayExists(gatewayService, gatewayId) {
71
+ try {
72
+ const response = await gatewayService.listGateways();
73
+ return response.gateways.some((gateway) => gateway.device.id === gatewayId);
74
+ }
75
+ catch {
76
+ return false;
77
+ }
78
+ }
79
+ //# sourceMappingURL=gateway-prompt.js.map
@@ -0,0 +1,120 @@
1
+ "use strict";
2
+ /**
3
+ * Input parsing utilities
4
+ * Converts string inputs to proper types and validates them
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.parsePort = parsePort;
8
+ exports.parseProtocol = parseProtocol;
9
+ exports.parseDeviceId = parseDeviceId;
10
+ exports.parseGatewayIds = parseGatewayIds;
11
+ exports.parseBoolean = parseBoolean;
12
+ exports.parseNumber = parseNumber;
13
+ exports.parseCommandOptions = parseCommandOptions;
14
+ const zod_1 = require("zod");
15
+ const schemas_1 = require("../../validation/schemas");
16
+ const errors_1 = require("../../utils/errors");
17
+ /**
18
+ * Parse port from string
19
+ */
20
+ function parsePort(value) {
21
+ if (typeof value === 'number') {
22
+ return schemas_1.portSchema.parse(value);
23
+ }
24
+ if (!value) {
25
+ throw new errors_1.ValidationError('Port is required');
26
+ }
27
+ const parsed = parseInt(value, 10);
28
+ if (isNaN(parsed)) {
29
+ throw new errors_1.ValidationError(`Invalid port number: ${value}`);
30
+ }
31
+ return schemas_1.portSchema.parse(parsed);
32
+ }
33
+ /**
34
+ * Parse protocol from string
35
+ */
36
+ function parseProtocol(value) {
37
+ if (!value) {
38
+ return 'http'; // Default
39
+ }
40
+ return schemas_1.protocolSchema.parse(value.toLowerCase());
41
+ }
42
+ /**
43
+ * Parse device ID from string
44
+ */
45
+ function parseDeviceId(value) {
46
+ if (!value) {
47
+ throw new errors_1.ValidationError('Device ID is required');
48
+ }
49
+ return schemas_1.deviceIdSchema.parse(value);
50
+ }
51
+ /**
52
+ * Parse gateway IDs from comma-separated string
53
+ */
54
+ function parseGatewayIds(value) {
55
+ if (!value) {
56
+ return [];
57
+ }
58
+ const ids = value.split(',').map((id) => id.trim()).filter((id) => id.length > 0);
59
+ return ids.map((id) => schemas_1.gatewayIdSchema.parse(id));
60
+ }
61
+ /**
62
+ * Parse boolean from string or boolean
63
+ */
64
+ function parseBoolean(value) {
65
+ if (typeof value === 'boolean') {
66
+ return value;
67
+ }
68
+ if (!value) {
69
+ return false;
70
+ }
71
+ const lower = value.toLowerCase();
72
+ return lower === 'true' || lower === '1' || lower === 'yes';
73
+ }
74
+ /**
75
+ * Parse number from string with validation
76
+ */
77
+ function parseNumber(value, min, max) {
78
+ if (typeof value === 'number') {
79
+ if (min !== undefined && value < min) {
80
+ throw new errors_1.ValidationError(`Value must be at least ${min}`);
81
+ }
82
+ if (max !== undefined && value > max) {
83
+ throw new errors_1.ValidationError(`Value must be at most ${max}`);
84
+ }
85
+ return value;
86
+ }
87
+ if (!value) {
88
+ throw new errors_1.ValidationError('Number is required');
89
+ }
90
+ const parsed = parseFloat(value);
91
+ if (isNaN(parsed)) {
92
+ throw new errors_1.ValidationError(`Invalid number: ${value}`);
93
+ }
94
+ if (min !== undefined && parsed < min) {
95
+ throw new errors_1.ValidationError(`Value must be at least ${min}`);
96
+ }
97
+ if (max !== undefined && parsed > max) {
98
+ throw new errors_1.ValidationError(`Value must be at most ${max}`);
99
+ }
100
+ return parsed;
101
+ }
102
+ /**
103
+ * Parse and validate command options
104
+ */
105
+ function parseCommandOptions(schema, options) {
106
+ try {
107
+ return schema.parse(options);
108
+ }
109
+ catch (error) {
110
+ if (error instanceof zod_1.z.ZodError) {
111
+ const issues = error.issues.map((issue) => {
112
+ const path = issue.path.length > 0 ? issue.path.join('.') : 'root';
113
+ return `${path}: ${issue.message}`;
114
+ });
115
+ throw new errors_1.ValidationError(`Invalid options: ${issues.join(', ')}`);
116
+ }
117
+ throw error;
118
+ }
119
+ }
120
+ //# sourceMappingURL=input-parser.js.map
@@ -0,0 +1,109 @@
1
+ "use strict";
2
+ /**
3
+ * Output formatting utilities
4
+ * Provides consistent formatting across commands
5
+ */
6
+ var __importDefault = (this && this.__importDefault) || function (mod) {
7
+ return (mod && mod.__esModule) ? mod : { "default": mod };
8
+ };
9
+ Object.defineProperty(exports, "__esModule", { value: true });
10
+ exports.formatApplication = formatApplication;
11
+ exports.formatGateway = formatGateway;
12
+ exports.formatList = formatList;
13
+ exports.formatError = formatError;
14
+ exports.formatSuccess = formatSuccess;
15
+ exports.formatWarning = formatWarning;
16
+ exports.formatInfo = formatInfo;
17
+ const chalk_1 = __importDefault(require("chalk"));
18
+ /**
19
+ * Format application output
20
+ */
21
+ function formatApplication(app) {
22
+ const statusColor = app.status === 'active' ? chalk_1.default.green : chalk_1.default.red;
23
+ const statusIcon = app.status === 'active' ? '●' : '○';
24
+ let output = '';
25
+ output += `${statusIcon} ${app.name}\n`;
26
+ output += chalk_1.default.gray(` ID: ${app.id}\n`);
27
+ output += chalk_1.default.gray(` Serving IP: ${chalk_1.default.cyan.bold(app.servingIp)}\n`);
28
+ output += chalk_1.default.gray(` Port: ${app.port}\n`);
29
+ output += chalk_1.default.gray(` Protocol: ${app.protocol.toUpperCase()}\n`);
30
+ output += chalk_1.default.gray(` Status: ${statusColor(app.status)}\n`);
31
+ if (app.workloadId) {
32
+ output += chalk_1.default.gray(` Workload ID: ${app.workloadId}\n`);
33
+ }
34
+ output += chalk_1.default.gray(` Created: ${new Date(app.createdAt).toLocaleString()}\n`);
35
+ if (app.description) {
36
+ output += chalk_1.default.gray(` Description: ${app.description}\n`);
37
+ }
38
+ output += chalk_1.default.cyan(` URL: ${app.protocol}://${app.servingIp}:${app.port}\n`);
39
+ return output;
40
+ }
41
+ /**
42
+ * Format gateway output
43
+ */
44
+ function formatGateway(gateway) {
45
+ const statusColor = gateway.ec2Instance?.state === 'running' ? chalk_1.default.green : chalk_1.default.red;
46
+ const statusIcon = gateway.ec2Instance?.state === 'running' ? '●' : '○';
47
+ let output = '';
48
+ output += `${statusIcon} ${gateway.device.name}\n`;
49
+ output += chalk_1.default.gray(` Device ID: ${gateway.device.id}\n`);
50
+ output += chalk_1.default.gray(` Description: ${gateway.device.description || 'No description'}\n`);
51
+ if (gateway.applicationCount !== undefined) {
52
+ output += chalk_1.default.gray(` Applications: ${gateway.applicationCount}\n`);
53
+ }
54
+ if (gateway.ec2Instance) {
55
+ output += chalk_1.default.gray(` EC2 Instance: ${gateway.ec2Instance.instanceId}\n`);
56
+ output += chalk_1.default.gray(` Public IP: ${chalk_1.default.cyan.bold(gateway.ec2Instance.publicIp)}\n`);
57
+ output += chalk_1.default.gray(` Private IP: ${gateway.ec2Instance.privateIp}\n`);
58
+ output += chalk_1.default.gray(` Region: ${gateway.ec2Instance.region}\n`);
59
+ output += chalk_1.default.gray(` State: ${statusColor(gateway.ec2Instance.state)}\n`);
60
+ }
61
+ output += chalk_1.default.gray(` Created: ${new Date(gateway.device.createdAt).toLocaleString()}\n`);
62
+ return output;
63
+ }
64
+ /**
65
+ * Format list of items as table-like output
66
+ */
67
+ function formatList(items, formatItem, header, emptyMessage) {
68
+ if (items.length === 0) {
69
+ return emptyMessage ? chalk_1.default.yellow(`⚠ ${emptyMessage}`) : '';
70
+ }
71
+ let output = '';
72
+ if (header) {
73
+ output += chalk_1.default.blue.bold(`${header}\n\n`);
74
+ }
75
+ items.forEach((item, index) => {
76
+ output += formatItem(item, index);
77
+ output += '\n';
78
+ });
79
+ return output;
80
+ }
81
+ /**
82
+ * Format error message with context
83
+ */
84
+ function formatError(message, suggestion) {
85
+ let output = chalk_1.default.red(`✗ ${message}\n`);
86
+ if (suggestion) {
87
+ output += chalk_1.default.gray(` ${suggestion}\n`);
88
+ }
89
+ return output;
90
+ }
91
+ /**
92
+ * Format success message
93
+ */
94
+ function formatSuccess(message) {
95
+ return chalk_1.default.green(`✓ ${message}\n`);
96
+ }
97
+ /**
98
+ * Format warning message
99
+ */
100
+ function formatWarning(message) {
101
+ return chalk_1.default.yellow(`⚠ ${message}\n`);
102
+ }
103
+ /**
104
+ * Format info message
105
+ */
106
+ function formatInfo(message) {
107
+ return chalk_1.default.blue(`${message}\n`);
108
+ }
109
+ //# sourceMappingURL=output-formatter.js.map
@@ -0,0 +1,99 @@
1
+ "use strict";
2
+ /**
3
+ * Application configuration management
4
+ * Centralizes all configuration loading and validation
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.loadConfig = loadConfig;
8
+ exports.validateConfig = validateConfig;
9
+ exports.getConfig = getConfig;
10
+ exports.resetConfig = resetConfig;
11
+ const urls_1 = require("../utils/urls");
12
+ // Production defaults - these are used when environment variables are not set
13
+ const DEFAULT_REGION = 'us-east-1';
14
+ const DEFAULT_INSTANCE_TYPE = 't3.micro';
15
+ /**
16
+ * Load configuration from environment variables and defaults
17
+ *
18
+ * Environment variable precedence:
19
+ * 1. User's environment variables (process.env.*) - highest priority
20
+ * 2. Production defaults (constants above) - fallback
21
+ *
22
+ * Note: Environment variables are read directly from process.env at runtime.
23
+ * Users can set these via shell/system environment variables.
24
+ *
25
+ * Available environment variables:
26
+ * - LOG_LEVEL: Set log level (default: 'INFO')
27
+ * Note: API_BASE_URL is now hardcoded based on stage (STAGE or SST_STAGE env var)
28
+ * - NO_COLOR: Disable colored output (set to '1' or 'true')
29
+ * - PLAIN_OUTPUT: Enable plain text output (set to '1' or 'true')
30
+ * - DEFAULT_REGION: Default AWS region (default: 'us-east-1')
31
+ * - DEFAULT_INSTANCE_TYPE: Default EC2 instance type (default: 't3.micro')
32
+ */
33
+ function loadConfig() {
34
+ const apiBaseUrl = (0, urls_1.getApiBaseUrl)();
35
+ const logLevel = process.env.LOG_LEVEL || 'INFO';
36
+ const colorEnabled = process.env.NO_COLOR !== '1' && process.env.NO_COLOR !== 'true';
37
+ const plain = process.env.PLAIN_OUTPUT === '1' || process.env.PLAIN_OUTPUT === 'true';
38
+ const defaultRegion = process.env.DEFAULT_REGION || DEFAULT_REGION;
39
+ const defaultInstanceType = process.env.DEFAULT_INSTANCE_TYPE || DEFAULT_INSTANCE_TYPE;
40
+ return {
41
+ apiBaseUrl,
42
+ logLevel,
43
+ colorEnabled,
44
+ plain,
45
+ defaultRegion,
46
+ defaultInstanceType,
47
+ };
48
+ }
49
+ /**
50
+ * Validate configuration
51
+ */
52
+ function validateConfig(config) {
53
+ const errors = [];
54
+ // Validate API URL
55
+ try {
56
+ new URL(config.apiBaseUrl);
57
+ }
58
+ catch {
59
+ errors.push(`Invalid API base URL: ${config.apiBaseUrl}`);
60
+ }
61
+ // Validate log level
62
+ const validLogLevels = ['DEBUG', 'INFO', 'WARN', 'ERROR', 'NONE', 'SILENT'];
63
+ if (!validLogLevels.includes(config.logLevel.toUpperCase())) {
64
+ errors.push(`Invalid log level: ${config.logLevel}. Valid values: ${validLogLevels.join(', ')}`);
65
+ }
66
+ // Validate region format (basic check)
67
+ if (!/^[a-z0-9-]+$/.test(config.defaultRegion)) {
68
+ errors.push(`Invalid default region format: ${config.defaultRegion}`);
69
+ }
70
+ // Validate instance type format (basic check)
71
+ if (!/^[a-z0-9.]+$/.test(config.defaultInstanceType)) {
72
+ errors.push(`Invalid default instance type format: ${config.defaultInstanceType}`);
73
+ }
74
+ return {
75
+ valid: errors.length === 0,
76
+ errors,
77
+ };
78
+ }
79
+ /**
80
+ * Get configuration singleton
81
+ */
82
+ let configInstance = null;
83
+ function getConfig() {
84
+ if (!configInstance) {
85
+ configInstance = loadConfig();
86
+ const validation = validateConfig(configInstance);
87
+ if (!validation.valid) {
88
+ console.warn('Configuration validation warnings:', validation.errors);
89
+ }
90
+ }
91
+ return configInstance;
92
+ }
93
+ /**
94
+ * Reset configuration (useful for testing)
95
+ */
96
+ function resetConfig() {
97
+ configInstance = null;
98
+ }
99
+ //# sourceMappingURL=app-config.js.map