env-secrets 0.1.10 → 0.3.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 (61) hide show
  1. package/.devcontainer/devcontainer.json +33 -0
  2. package/.dockerignore +9 -0
  3. package/.eslintignore +4 -2
  4. package/.github/dependabot.yml +4 -0
  5. package/.github/workflows/build-main.yml +6 -2
  6. package/.github/workflows/deploy-docs.yml +50 -0
  7. package/.github/workflows/e2e-tests.yaml +54 -0
  8. package/.github/workflows/lint.yaml +6 -2
  9. package/.github/workflows/release.yml +13 -3
  10. package/.github/workflows/snyk.yaml +5 -1
  11. package/.github/workflows/unittests.yaml +18 -6
  12. package/.lintstagedrc +2 -7
  13. package/.prettierignore +6 -0
  14. package/AGENTS.md +149 -0
  15. package/Dockerfile +14 -0
  16. package/README.md +507 -36
  17. package/__e2e__/README.md +160 -0
  18. package/__e2e__/index.test.ts +339 -0
  19. package/__e2e__/setup.ts +58 -0
  20. package/__e2e__/utils/debug-logger.ts +45 -0
  21. package/__e2e__/utils/test-utils.ts +645 -0
  22. package/__tests__/index.test.ts +573 -31
  23. package/__tests__/vaults/secretsmanager.test.ts +460 -0
  24. package/__tests__/vaults/utils.test.ts +183 -0
  25. package/__tests__/version.test.ts +8 -0
  26. package/dist/index.js +36 -10
  27. package/dist/vaults/secretsmanager.js +44 -43
  28. package/dist/vaults/utils.js +2 -2
  29. package/docker-compose.yaml +29 -0
  30. package/docs/AWS.md +257 -0
  31. package/jest.config.js +4 -1
  32. package/jest.e2e.config.js +8 -0
  33. package/package.json +18 -10
  34. package/src/index.ts +44 -10
  35. package/src/vaults/secretsmanager.ts +48 -48
  36. package/src/vaults/utils.ts +6 -4
  37. package/website/docs/advanced-usage.mdx +399 -0
  38. package/website/docs/best-practices.mdx +416 -0
  39. package/website/docs/cli-reference.mdx +204 -0
  40. package/website/docs/examples.mdx +960 -0
  41. package/website/docs/faq.mdx +302 -0
  42. package/website/docs/index.mdx +56 -0
  43. package/website/docs/installation.mdx +30 -0
  44. package/website/docs/overview.mdx +17 -0
  45. package/website/docs/production-deployment.mdx +622 -0
  46. package/website/docs/providers/aws-secrets-manager.mdx +28 -0
  47. package/website/docs/security.mdx +122 -0
  48. package/website/docs/troubleshooting.mdx +236 -0
  49. package/website/docs/tutorials/local-dev/devcontainer-localstack.mdx +31 -0
  50. package/website/docs/tutorials/local-dev/docker-compose.mdx +22 -0
  51. package/website/docs/tutorials/local-dev/nextjs.mdx +18 -0
  52. package/website/docs/tutorials/local-dev/node-python-go.mdx +39 -0
  53. package/website/docs/tutorials/local-dev/quickstart.mdx +23 -0
  54. package/website/docusaurus.config.ts +89 -0
  55. package/website/package.json +21 -0
  56. package/website/sidebars.ts +33 -0
  57. package/website/src/css/custom.css +1 -0
  58. package/website/static/img/env-secrets.png +0 -0
  59. package/website/static/img/favicon.ico +0 -0
  60. package/website/static/img/logo.svg +4 -0
  61. package/website/yarn.lock +8764 -0
@@ -0,0 +1,160 @@
1
+ # End-to-End Tests
2
+
3
+ This directory contains comprehensive end-to-end tests for the env-secrets CLI tool.
4
+
5
+ ## Prerequisites
6
+
7
+ ### LocalStack Setup
8
+
9
+ The e2e tests use LocalStack to emulate AWS Secrets Manager. You need to have LocalStack running before executing the tests.
10
+
11
+ #### Using Docker Compose (Recommended)
12
+
13
+ ```bash
14
+ # Start LocalStack
15
+ docker-compose up -d localstack
16
+
17
+ # Wait for LocalStack to be ready
18
+ docker-compose logs -f localstack
19
+ ```
20
+
21
+ #### Manual LocalStack Setup
22
+
23
+ ```bash
24
+ # Install LocalStack
25
+ pip install localstack
26
+
27
+ # Start LocalStack
28
+ localstack start
29
+ ```
30
+
31
+ ### awslocal Setup
32
+
33
+ The tests require `awslocal` to be installed, which is a wrapper around AWS CLI that automatically points to LocalStack:
34
+
35
+ ```bash
36
+ # Install awslocal using pip (recommended)
37
+ pip install awscli-local
38
+
39
+ # Or using npm
40
+ npm install -g awscli-local
41
+
42
+ # Verify installation
43
+ awslocal --version
44
+
45
+ # Test connectivity to LocalStack
46
+ awslocal sts get-caller-identity
47
+ ```
48
+
49
+ **Note**: The tests will automatically check if `awslocal` is installed and provide helpful error messages if it's missing.
50
+
51
+ ## Environment Variables
52
+
53
+ The following environment variables are used by the e2e tests:
54
+
55
+ - `LOCALSTACK_URL`: LocalStack endpoint (default: `http://localhost:4566`)
56
+ - `AWS_ACCESS_KEY_ID`: AWS access key (default: `test`)
57
+ - `AWS_SECRET_ACCESS_KEY`: AWS secret key (default: `test`)
58
+ - `AWS_DEFAULT_REGION`: AWS region (default: `us-east-1`)
59
+ - `DEBUG`: Enable debug output (optional)
60
+
61
+ **Note**: The tests automatically clean up AWS environment variables (like `AWS_PROFILE`, `AWS_SESSION_TOKEN`, etc.) to ensure a clean test environment.
62
+
63
+ ## Running Tests
64
+
65
+ ### Run All E2E Tests
66
+
67
+ ```bash
68
+ npm run test:e2e
69
+ ```
70
+
71
+ ### Run E2E Tests with Coverage
72
+
73
+ ```bash
74
+ npm run test:e2e:coverage
75
+ ```
76
+
77
+ ### Run Specific Test File
78
+
79
+ ```bash
80
+ npm run build
81
+ npx jest --config jest.e2e.config.js __e2e__/index.test.ts
82
+ ```
83
+
84
+ ### Run Tests with Debug Output
85
+
86
+ ```bash
87
+ DEBUG=1 npm run test:e2e
88
+ ```
89
+
90
+ ## Test Structure
91
+
92
+ ### Test Categories
93
+
94
+ 1. **CLI Help Commands**: Tests for help, version, and general CLI functionality
95
+ 2. **AWS Secrets Manager Integration**: Tests for secret retrieval using different credential methods
96
+ 3. **Output to File**: Tests for writing secrets to files
97
+ 4. **Program Execution**: Tests for executing programs with injected environment variables
98
+ 5. **Error Handling**: Tests for various error scenarios
99
+
100
+ ### Test Utilities
101
+
102
+ The `utils/test-utils.ts` file provides helper functions:
103
+
104
+ - `LocalStackHelper`: Manages LocalStack operations (create/delete secrets, wait for readiness)
105
+ - `cli()`: Execute CLI commands
106
+ - `cliWithEnv()`: Execute CLI commands with custom environment variables
107
+ - `createTempFile()` / `cleanupTempFile()`: Manage temporary files
108
+ - `createTestProfile()` / `restoreTestProfile()`: Manage AWS profiles
109
+
110
+ ### Test Secrets
111
+
112
+ The tests create and use several test secrets:
113
+
114
+ - `test-secret-basic`: Basic JSON secret with API key and database URL
115
+ - `test-secret-simple`: Simple string secret
116
+ - `test-secret-complex`: Complex JSON with nested objects and arrays
117
+ - `test-secret-special-chars`: Secret with special characters
118
+
119
+ ## Troubleshooting
120
+
121
+ ### LocalStack Not Starting
122
+
123
+ If LocalStack fails to start:
124
+
125
+ 1. Check if port 4566 is available
126
+ 2. Ensure Docker is running
127
+ 3. Check LocalStack logs: `docker-compose logs localstack`
128
+
129
+ ### awslocal Issues
130
+
131
+ If awslocal commands fail:
132
+
133
+ 1. Verify awslocal is installed: `awslocal --version`
134
+ 2. Test LocalStack connectivity: `awslocal sts get-caller-identity`
135
+ 3. Check if LocalStack is running: `docker-compose ps`
136
+
137
+ ### Test Failures
138
+
139
+ Common issues and solutions:
140
+
141
+ 1. **Timeout errors**: Increase timeout in `jest.e2e.config.js`
142
+ 2. **Permission errors**: Ensure proper file permissions for AWS credentials
143
+ 3. **Network errors**: Check LocalStack endpoint URL and connectivity
144
+
145
+ ## Continuous Integration
146
+
147
+ The e2e tests are designed to run in CI environments. Make sure to:
148
+
149
+ 1. Start LocalStack before running tests
150
+ 2. Set appropriate environment variables
151
+ 3. Install AWS CLI in the CI environment
152
+ 4. Allow sufficient time for LocalStack to start
153
+
154
+ ## Coverage Reports
155
+
156
+ Coverage reports are generated in the `coverage-e2e/` directory:
157
+
158
+ - `coverage-e2e/lcov-report/index.html`: HTML coverage report
159
+ - `coverage-e2e/lcov.info`: LCOV format for CI integration
160
+ - `coverage-e2e/coverage-final.json`: JSON coverage data
@@ -0,0 +1,339 @@
1
+ import {
2
+ LocalStackHelper,
3
+ cli,
4
+ cliWithEnv,
5
+ createTempFile,
6
+ cleanupTempFile,
7
+ createTestProfile,
8
+ restoreTestProfile,
9
+ TestSecret,
10
+ CreatedSecret
11
+ } from './utils/test-utils';
12
+ import { debugLog } from './utils/debug-logger';
13
+ import * as fs from 'fs';
14
+ import * as path from 'path';
15
+ import * as os from 'os';
16
+
17
+ describe('End-to-End Tests', () => {
18
+ let localStack: LocalStackHelper;
19
+ let awsDir: string | undefined;
20
+
21
+ beforeAll(async () => {
22
+ localStack = new LocalStackHelper();
23
+ await localStack.waitForLocalStack();
24
+
25
+ // Create test AWS profile
26
+ awsDir = createTestProfile();
27
+ });
28
+
29
+ afterAll(async () => {
30
+ // Clean up all secrets created during this test run
31
+ await localStack.cleanupRunSecrets();
32
+
33
+ // Restore AWS profile
34
+ restoreTestProfile(awsDir);
35
+ });
36
+
37
+ // Helper function to create a secret for a test
38
+ const createTestSecret = async (
39
+ secret: TestSecret,
40
+ region?: string
41
+ ): Promise<CreatedSecret> => {
42
+ return await localStack.createSecret(secret, region);
43
+ };
44
+
45
+ // Helper function to create LocalStack environment variables
46
+ const getLocalStackEnv = (overrides: Record<string, string> = {}) => ({
47
+ AWS_ENDPOINT_URL: process.env.LOCALSTACK_URL || 'http://localhost:4566',
48
+ AWS_ACCESS_KEY_ID: 'test',
49
+ AWS_SECRET_ACCESS_KEY: 'test',
50
+ AWS_DEFAULT_REGION: 'us-east-1',
51
+ ...overrides
52
+ });
53
+
54
+ describe('CLI Help Commands', () => {
55
+ test('should show general help', async () => {
56
+ const result = await cli(['-h']);
57
+ expect(result.code).toBe(0);
58
+ expect(result.stdout).toContain('env-secrets');
59
+ expect(result.stdout).toContain('pull secrets from vaults');
60
+ });
61
+
62
+ test('should show AWS command help', async () => {
63
+ const result = await cli(['aws', '-h']);
64
+ expect(result.code).toBe(0);
65
+ expect(result.stdout).toContain('get secrets from AWS secrets manager');
66
+ expect(result.stdout).toContain('--secret');
67
+ });
68
+
69
+ test('should show version', async () => {
70
+ const result = await cli(['--version']);
71
+ expect(result.code).toBe(0);
72
+ expect(result.stdout).toMatch(/^\d+\.\d+\.\d+/);
73
+ });
74
+ });
75
+
76
+ describe('AWS Secrets Manager Integration', () => {
77
+ describe('Using Default AWS Credentials', () => {
78
+ test('should retrieve basic JSON secret', async () => {
79
+ const secret = await createTestSecret({
80
+ name: 'test-secret-basic',
81
+ value:
82
+ '{"API_KEY": "secret123", "DATABASE_URL": "postgres://localhost:5432/test"}',
83
+ description: 'Basic test secret with API key and database URL'
84
+ });
85
+
86
+ const result = await cliWithEnv(
87
+ ['aws', '-s', secret.prefixedName],
88
+ getLocalStackEnv()
89
+ );
90
+
91
+ expect(result.code).toBe(0);
92
+ expect(result.stderr).toBe('');
93
+ });
94
+
95
+ test('should retrieve simple string secret', async () => {
96
+ const secret = await createTestSecret({
97
+ name: 'test-secret-simple',
98
+ value: 'simple-string-value',
99
+ description: 'Simple string secret'
100
+ });
101
+
102
+ const result = await cliWithEnv(
103
+ ['aws', '-s', secret.prefixedName],
104
+ getLocalStackEnv()
105
+ );
106
+
107
+ expect(result.code).toBe(0);
108
+ expect(result.stderr).toBe('');
109
+ });
110
+
111
+ test('should retrieve complex JSON secret', async () => {
112
+ const secret = await createTestSecret({
113
+ name: 'test-secret-complex',
114
+ value:
115
+ '{"NESTED": {"KEY": "value"}, "ARRAY": [1, 2, 3], "BOOLEAN": true, "NUMBER": 42}',
116
+ description: 'Complex JSON secret'
117
+ });
118
+
119
+ const result = await cliWithEnv(
120
+ ['aws', '-s', secret.prefixedName],
121
+ getLocalStackEnv()
122
+ );
123
+
124
+ expect(result.code).toBe(0);
125
+ expect(result.stderr).toBe('');
126
+ });
127
+
128
+ test('should retrieve secret with special characters', async () => {
129
+ const secret = await createTestSecret({
130
+ name: 'test-secret-special-chars',
131
+ value:
132
+ '{"PASSWORD": "p@ssw0rd!#$%", "URL": "https://api.example.com/v1?key=value&other=test"}',
133
+ description: 'Secret with special characters'
134
+ });
135
+
136
+ const result = await cliWithEnv(
137
+ ['aws', '-s', secret.prefixedName],
138
+ getLocalStackEnv()
139
+ );
140
+
141
+ expect(result.code).toBe(0);
142
+ expect(result.stderr).toBe('');
143
+ });
144
+
145
+ test('should handle non-existent secret', async () => {
146
+ const result = await cliWithEnv(
147
+ ['aws', '-s', 'non-existent-secret'],
148
+ getLocalStackEnv()
149
+ );
150
+
151
+ expect(result.code).toBe(0);
152
+ expect(result.stderr).toContain('non-existent-secret not found');
153
+ });
154
+
155
+ test('should work with custom region', async () => {
156
+ const secret = await createTestSecret(
157
+ {
158
+ name: `test-secret-region-${Date.now()}`,
159
+ value:
160
+ '{"API_KEY": "secret123", "DATABASE_URL": "postgres://localhost:5432/test"}',
161
+ description: 'Basic test secret for us-west-2'
162
+ },
163
+ 'us-west-2'
164
+ );
165
+
166
+ const result = await cliWithEnv(
167
+ ['aws', '-s', secret.prefixedName, '-r', 'us-west-2'],
168
+ getLocalStackEnv()
169
+ );
170
+
171
+ expect(result.code).toBe(0);
172
+ expect(result.stderr).toBe('');
173
+ });
174
+ });
175
+
176
+ describe('Using AWS Profile', () => {
177
+ test('should retrieve secret using default profile', async () => {
178
+ const secret = await createTestSecret({
179
+ name: 'test-secret-profile',
180
+ value:
181
+ '{"API_KEY": "secret123", "DATABASE_URL": "postgres://localhost:5432/test"}',
182
+ description: 'Secret for profile test'
183
+ });
184
+
185
+ const result = await cliWithEnv(
186
+ ['aws', '-s', secret.prefixedName],
187
+ getLocalStackEnv()
188
+ );
189
+
190
+ expect(result.code).toBe(0);
191
+ expect(result.stderr).toBe('');
192
+ });
193
+
194
+ test('should retrieve secret using custom profile', async () => {
195
+ const secret = await createTestSecret({
196
+ name: 'test-secret-profile-custom',
197
+ value:
198
+ '{"API_KEY": "secret123", "DATABASE_URL": "postgres://localhost:5432/test"}',
199
+ description: 'Secret for custom profile test'
200
+ });
201
+
202
+ const result = await cliWithEnv(
203
+ ['aws', '-s', secret.prefixedName, '-p', 'env-secrets-test'],
204
+ getLocalStackEnv()
205
+ );
206
+
207
+ expect(result.code).toBe(0);
208
+ expect(result.stderr).toBe('');
209
+ });
210
+ });
211
+
212
+ describe('Output to File', () => {
213
+ test('should write secrets to file', async () => {
214
+ debugLog('Starting test...');
215
+
216
+ const secret = await createTestSecret({
217
+ name: `test-secret-file-${Date.now()}`,
218
+ value:
219
+ '{"API_KEY": "secret123", "DATABASE_URL": "postgres://localhost:5432/test"}',
220
+ description: 'Secret for file output test'
221
+ });
222
+ debugLog('Secret created successfully');
223
+
224
+ const tempFile = path.join(
225
+ os.tmpdir(),
226
+ `env-secrets-test-${Date.now()}.env`
227
+ );
228
+
229
+ const result = await cliWithEnv(
230
+ ['aws', '-s', secret.prefixedName, '-o', tempFile],
231
+ getLocalStackEnv()
232
+ );
233
+
234
+ debugLog('CLI result:', {
235
+ code: result.code,
236
+ stdout: result.stdout,
237
+ stderr: result.stderr
238
+ });
239
+ expect(result.code).toBe(0);
240
+ expect(result.stdout).toContain(`Secrets written to ${tempFile}`);
241
+
242
+ // Verify file contents
243
+ const fileContent = fs.readFileSync(tempFile, 'utf8');
244
+ expect(fileContent).toContain('API_KEY=secret123');
245
+ expect(fileContent).toContain(
246
+ 'DATABASE_URL=postgres://localhost:5432/test'
247
+ );
248
+
249
+ cleanupTempFile(tempFile);
250
+ });
251
+
252
+ test('should not overwrite existing file', async () => {
253
+ const tempFile = createTempFile('existing content');
254
+
255
+ try {
256
+ const result = await cliWithEnv(
257
+ ['aws', '-s', 'test-secret-basic', '-o', tempFile],
258
+ getLocalStackEnv()
259
+ );
260
+
261
+ expect(result.code).toBe(1);
262
+ expect(result.stderr).toContain(
263
+ 'already exists and will not be overwritten'
264
+ );
265
+
266
+ // Verify file was not modified
267
+ const fileContent = fs.readFileSync(tempFile, 'utf8');
268
+ expect(fileContent).toBe('existing content');
269
+ } finally {
270
+ cleanupTempFile(tempFile);
271
+ }
272
+ });
273
+ });
274
+
275
+ describe('Program Execution', () => {
276
+ test('should execute program with injected environment variables', async () => {
277
+ const secret = await createTestSecret({
278
+ name: `test-secret-exec-${Date.now()}`,
279
+ value:
280
+ '{"API_KEY": "secret123", "DATABASE_URL": "postgres://localhost:5432/test"}',
281
+ description: 'Secret for program execution test'
282
+ });
283
+
284
+ const result = await cliWithEnv(
285
+ ['aws', '-s', secret.prefixedName, 'echo', '$API_KEY'],
286
+ getLocalStackEnv()
287
+ );
288
+
289
+ expect(result.code).toBe(0);
290
+
291
+ // In test mode, the CLI outputs the environment variables as JSON
292
+ const envVars = JSON.parse(result.stdout.trim());
293
+ expect(envVars.API_KEY).toBe('secret123');
294
+ expect(envVars.DATABASE_URL).toBe('postgres://localhost:5432/test');
295
+ });
296
+
297
+ test('should handle program execution errors gracefully', async () => {
298
+ const secret = await createTestSecret({
299
+ name: `test-secret-error-${Date.now()}`,
300
+ value: '{"API_KEY": "secret123"}',
301
+ description: 'Secret for error handling test'
302
+ });
303
+
304
+ const result = await cliWithEnv(
305
+ ['aws', '-s', secret.prefixedName, 'nonexistent-command'],
306
+ getLocalStackEnv()
307
+ );
308
+
309
+ expect(result.code).toBe(0);
310
+
311
+ // In test mode, the CLI outputs the environment variables as JSON
312
+ const envVars = JSON.parse(result.stdout.trim());
313
+ expect(envVars.API_KEY).toBe('secret123');
314
+ });
315
+ });
316
+
317
+ describe('Error Handling', () => {
318
+ test('should handle invalid AWS credentials', async () => {
319
+ const result = await cliWithEnv(
320
+ ['aws', '-s', 'test-secret-basic'],
321
+ getLocalStackEnv({
322
+ AWS_ACCESS_KEY_ID: 'invalid',
323
+ AWS_SECRET_ACCESS_KEY: 'invalid'
324
+ })
325
+ );
326
+
327
+ // LocalStack accepts any credentials, so this should succeed
328
+ expect(result.code).toBe(0);
329
+ });
330
+
331
+ test('should handle missing secret parameter', async () => {
332
+ const result = await cliWithEnv(['aws'], getLocalStackEnv());
333
+
334
+ expect(result.code).toBe(1);
335
+ expect(result.stderr).toContain('required option');
336
+ });
337
+ });
338
+ });
339
+ });
@@ -0,0 +1,58 @@
1
+ // E2E Test Setup
2
+ // This file runs before all e2e tests
3
+
4
+ import { LocalStackHelper, checkAwslocalInstalled } from './utils/test-utils';
5
+ import { debugLog, debugError } from './utils/debug-logger';
6
+
7
+ // Increase timeout for e2e tests
8
+ jest.setTimeout(30000);
9
+
10
+ // Global setup for e2e tests
11
+ beforeAll(async () => {
12
+ debugLog('Setting up E2E test environment...');
13
+
14
+ // Check if awslocal is installed
15
+ await checkAwslocalInstalled();
16
+
17
+ // Check if LocalStack is available
18
+ const localStack = new LocalStackHelper();
19
+ try {
20
+ await localStack.waitForLocalStack();
21
+ debugLog('LocalStack is ready for E2E tests');
22
+ } catch (error) {
23
+ debugError(
24
+ 'LocalStack is not available, some tests may fail:',
25
+ error.message
26
+ );
27
+ process.exit(1);
28
+ }
29
+ });
30
+
31
+ // Global cleanup
32
+ afterAll(async () => {
33
+ debugLog('Cleaning up E2E test environment...');
34
+ });
35
+
36
+ // Suppress console.log during tests unless DEBUG is set
37
+ // But always show console.error and console.warn for debugging
38
+ const originalConsoleLog = console.log;
39
+ const originalConsoleInfo = console.info;
40
+ const originalConsoleDebug = console.debug;
41
+
42
+ beforeEach(() => {
43
+ if (!process.env.DEBUG) {
44
+ console.log = jest.fn();
45
+ console.info = jest.fn();
46
+ console.debug = jest.fn();
47
+ // Keep console.error and console.warn enabled for debugging
48
+ }
49
+ });
50
+
51
+ afterEach(() => {
52
+ if (!process.env.DEBUG) {
53
+ console.log = originalConsoleLog;
54
+ console.info = originalConsoleInfo;
55
+ console.debug = originalConsoleDebug;
56
+ // console.error and console.warn are already enabled
57
+ }
58
+ });
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Debug logging utility for e2e tests
3
+ * Only outputs logs when DEBUG environment variable is set
4
+ */
5
+
6
+ export class DebugLogger {
7
+ private static isDebugEnabled(): boolean {
8
+ return process.env.DEBUG === 'true' || process.env.DEBUG === '1';
9
+ }
10
+
11
+ static log(message: string, ...args: any[]): void {
12
+ if (DebugLogger.isDebugEnabled()) {
13
+ console.log(message, ...args);
14
+ }
15
+ }
16
+
17
+ static error(message: string, ...args: any[]): void {
18
+ // Always show errors for debugging
19
+ console.error(message, ...args);
20
+ }
21
+
22
+ static warn(message: string, ...args: any[]): void {
23
+ // Always show warnings for debugging
24
+ console.warn(message, ...args);
25
+ }
26
+
27
+ static info(message: string, ...args: any[]): void {
28
+ if (DebugLogger.isDebugEnabled()) {
29
+ console.info(message, ...args);
30
+ }
31
+ }
32
+
33
+ static debug(message: string, ...args: any[]): void {
34
+ if (DebugLogger.isDebugEnabled()) {
35
+ console.debug(message, ...args);
36
+ }
37
+ }
38
+ }
39
+
40
+ // Export convenience functions
41
+ export const debugLog = DebugLogger.log;
42
+ export const debugError = DebugLogger.error;
43
+ export const debugWarn = DebugLogger.warn;
44
+ export const debugInfo = DebugLogger.info;
45
+ export const debugDebug = DebugLogger.debug;