@hazeljs/config 0.2.0-beta.55 → 0.2.0-beta.56

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.
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=config.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.test.d.ts","sourceRoot":"","sources":["../src/config.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,287 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ /// <reference types="jest" />
37
+ const fs = __importStar(require("fs"));
38
+ const dotenv = __importStar(require("dotenv"));
39
+ const config_service_1 = require("./config.service");
40
+ const config_module_1 = require("./config.module");
41
+ jest.mock('fs');
42
+ jest.mock('dotenv');
43
+ // Use __esModule: true so default import (`import logger from`) resolves correctly
44
+ jest.mock('@hazeljs/core', () => ({
45
+ __esModule: true,
46
+ Service: () => () => undefined,
47
+ HazelModule: () => () => undefined,
48
+ logger: { info: jest.fn(), debug: jest.fn(), warn: jest.fn(), error: jest.fn() },
49
+ default: { info: jest.fn(), debug: jest.fn(), warn: jest.fn(), error: jest.fn() },
50
+ }));
51
+ const mockedFs = fs;
52
+ const mockedDotenv = dotenv;
53
+ describe('ConfigService', () => {
54
+ beforeEach(() => {
55
+ // Reset static options between tests
56
+ config_service_1.ConfigService.setOptions({});
57
+ // Clear mock call history
58
+ jest.clearAllMocks();
59
+ // Default: fs.existsSync returns false (no .env files)
60
+ mockedFs.existsSync.mockReturnValue(false);
61
+ // Default: dotenv.config returns empty parsed
62
+ mockedDotenv.config.mockReturnValue({ parsed: {} });
63
+ });
64
+ describe('get()', () => {
65
+ it('returns undefined for missing key', () => {
66
+ const svc = new config_service_1.ConfigService();
67
+ expect(svc.get('MISSING_KEY_HAZEL_TEST')).toBeUndefined();
68
+ });
69
+ it('returns default value for missing key', () => {
70
+ const svc = new config_service_1.ConfigService();
71
+ expect(svc.get('MISSING_KEY_HAZEL_TEST', 'default')).toBe('default');
72
+ });
73
+ it('returns value for existing env var', () => {
74
+ process.env.__HAZEL_TEST_VAR__ = 'hello';
75
+ const svc = new config_service_1.ConfigService();
76
+ expect(svc.get('__HAZEL_TEST_VAR__')).toBe('hello');
77
+ delete process.env.__HAZEL_TEST_VAR__;
78
+ });
79
+ });
80
+ describe('set()', () => {
81
+ it('sets a configuration value', () => {
82
+ const svc = new config_service_1.ConfigService();
83
+ svc.set('myKey', 'myValue');
84
+ expect(svc.get('myKey')).toBe('myValue');
85
+ });
86
+ it('overwrites existing value', () => {
87
+ const svc = new config_service_1.ConfigService();
88
+ svc.set('myKey', 'first');
89
+ svc.set('myKey', 'second');
90
+ expect(svc.get('myKey')).toBe('second');
91
+ });
92
+ });
93
+ describe('has()', () => {
94
+ it('returns false for missing key', () => {
95
+ const svc = new config_service_1.ConfigService();
96
+ expect(svc.has('NO_SUCH_KEY_XYZ')).toBe(false);
97
+ });
98
+ it('returns true for existing key', () => {
99
+ const svc = new config_service_1.ConfigService();
100
+ svc.set('existingKey', 'val');
101
+ expect(svc.has('existingKey')).toBe(true);
102
+ });
103
+ });
104
+ describe('getAll()', () => {
105
+ it('returns all configuration values', () => {
106
+ const svc = new config_service_1.ConfigService();
107
+ svc.set('k1', 'v1');
108
+ svc.set('k2', 'v2');
109
+ const all = svc.getAll();
110
+ expect(all.k1).toBe('v1');
111
+ expect(all.k2).toBe('v2');
112
+ });
113
+ it('returns a copy, not a reference', () => {
114
+ const svc = new config_service_1.ConfigService();
115
+ svc.set('k', 'v');
116
+ const all = svc.getAll();
117
+ all.k = 'mutated';
118
+ expect(svc.get('k')).toBe('v');
119
+ });
120
+ });
121
+ describe('getOrThrow()', () => {
122
+ it('returns value when key exists', () => {
123
+ const svc = new config_service_1.ConfigService();
124
+ svc.set('req', 'present');
125
+ expect(svc.getOrThrow('req')).toBe('present');
126
+ });
127
+ it('throws when key is missing', () => {
128
+ const svc = new config_service_1.ConfigService();
129
+ expect(() => svc.getOrThrow('DEFINITELY_NOT_SET_HAZEL')).toThrow('Configuration key "DEFINITELY_NOT_SET_HAZEL" is required but not found');
130
+ });
131
+ });
132
+ describe('dot-notation nested keys', () => {
133
+ it('resolves nested config via dot notation', () => {
134
+ config_service_1.ConfigService.setOptions({
135
+ ignoreEnvVars: true,
136
+ ignoreEnvFile: true,
137
+ load: [() => ({ database: { host: 'localhost', port: 5432 } })],
138
+ });
139
+ const svc = new config_service_1.ConfigService();
140
+ expect(svc.get('database.host')).toBe('localhost');
141
+ expect(svc.get('database.port')).toBe(5432);
142
+ });
143
+ it('returns undefined for missing nested key', () => {
144
+ config_service_1.ConfigService.setOptions({
145
+ ignoreEnvVars: true,
146
+ ignoreEnvFile: true,
147
+ load: [() => ({ database: { host: 'localhost' } })],
148
+ });
149
+ const svc = new config_service_1.ConfigService();
150
+ expect(svc.get('database.missing')).toBeUndefined();
151
+ });
152
+ it('returns undefined when parent key does not exist', () => {
153
+ config_service_1.ConfigService.setOptions({ ignoreEnvVars: true, ignoreEnvFile: true });
154
+ const svc = new config_service_1.ConfigService();
155
+ expect(svc.get('no.such.path')).toBeUndefined();
156
+ });
157
+ });
158
+ describe('custom loaders', () => {
159
+ it('merges custom loader config', () => {
160
+ config_service_1.ConfigService.setOptions({
161
+ ignoreEnvVars: true,
162
+ ignoreEnvFile: true,
163
+ load: [() => ({ APP_NAME: 'hazel' }), () => ({ APP_VERSION: '1.0.0' })],
164
+ });
165
+ const svc = new config_service_1.ConfigService();
166
+ expect(svc.get('APP_NAME')).toBe('hazel');
167
+ expect(svc.get('APP_VERSION')).toBe('1.0.0');
168
+ });
169
+ });
170
+ describe('ignoreEnvVars', () => {
171
+ it('skips loading process.env when ignoreEnvVars is true', () => {
172
+ process.env.__HAZEL_IGNORE_TEST__ = 'should-not-appear';
173
+ config_service_1.ConfigService.setOptions({ ignoreEnvVars: true, ignoreEnvFile: true });
174
+ const svc = new config_service_1.ConfigService();
175
+ expect(svc.get('__HAZEL_IGNORE_TEST__')).toBeUndefined();
176
+ delete process.env.__HAZEL_IGNORE_TEST__;
177
+ });
178
+ });
179
+ describe('ignoreEnvFile', () => {
180
+ it('skips .env loading when ignoreEnvFile is true', () => {
181
+ config_service_1.ConfigService.setOptions({ ignoreEnvFile: true });
182
+ new config_service_1.ConfigService();
183
+ expect(mockedFs.existsSync).not.toHaveBeenCalled();
184
+ });
185
+ });
186
+ describe('.env file loading', () => {
187
+ it('loads values from an existing .env file', () => {
188
+ mockedFs.existsSync.mockReturnValue(true);
189
+ mockedDotenv.config.mockReturnValue({ parsed: { FROM_ENV_FILE: 'env-value' } });
190
+ config_service_1.ConfigService.setOptions({ ignoreEnvVars: true, envFilePath: '/custom/.env' });
191
+ const svc = new config_service_1.ConfigService();
192
+ expect(mockedDotenv.config).toHaveBeenCalled();
193
+ expect(svc.get('FROM_ENV_FILE')).toBe('env-value');
194
+ });
195
+ it('handles multiple env file paths', () => {
196
+ mockedFs.existsSync.mockReturnValue(true);
197
+ mockedDotenv.config
198
+ .mockReturnValueOnce({ parsed: { KEY_A: 'a' } })
199
+ .mockReturnValueOnce({ parsed: { KEY_B: 'b' } });
200
+ config_service_1.ConfigService.setOptions({
201
+ ignoreEnvVars: true,
202
+ envFilePath: ['/path/one.env', '/path/two.env'],
203
+ });
204
+ const svc = new config_service_1.ConfigService();
205
+ expect(mockedDotenv.config).toHaveBeenCalledTimes(2);
206
+ expect(svc.get('KEY_A')).toBe('a');
207
+ expect(svc.get('KEY_B')).toBe('b');
208
+ });
209
+ it('handles dotenv parse errors gracefully', () => {
210
+ mockedFs.existsSync.mockReturnValue(true);
211
+ mockedDotenv.config.mockReturnValue({ error: new Error('parse error') });
212
+ // Should not throw
213
+ config_service_1.ConfigService.setOptions({ ignoreEnvVars: true, envFilePath: '/bad.env' });
214
+ expect(() => new config_service_1.ConfigService()).not.toThrow();
215
+ });
216
+ it('skips non-existent env files gracefully', () => {
217
+ mockedFs.existsSync.mockReturnValue(false);
218
+ config_service_1.ConfigService.setOptions({ ignoreEnvVars: true, envFilePath: '/missing.env' });
219
+ expect(() => new config_service_1.ConfigService()).not.toThrow();
220
+ expect(mockedDotenv.config).not.toHaveBeenCalled();
221
+ });
222
+ });
223
+ describe('validation schema', () => {
224
+ it('uses validated config value when validation passes', () => {
225
+ const schema = {
226
+ validate: (config) => ({
227
+ value: { ...config, EXTRA_KEY: 'added-by-schema' },
228
+ }),
229
+ };
230
+ config_service_1.ConfigService.setOptions({
231
+ ignoreEnvVars: true,
232
+ ignoreEnvFile: true,
233
+ validationSchema: schema,
234
+ });
235
+ const svc = new config_service_1.ConfigService();
236
+ expect(svc.get('EXTRA_KEY')).toBe('added-by-schema');
237
+ });
238
+ it('throws when validation fails and abortEarly is not set', () => {
239
+ const schema = {
240
+ validate: () => ({
241
+ error: new Error('validation failed'),
242
+ value: {},
243
+ }),
244
+ };
245
+ config_service_1.ConfigService.setOptions({
246
+ ignoreEnvVars: true,
247
+ ignoreEnvFile: true,
248
+ validationSchema: schema,
249
+ });
250
+ expect(() => new config_service_1.ConfigService()).toThrow('Configuration validation error: validation failed');
251
+ });
252
+ it('does not throw when validation fails and abortEarly is true', () => {
253
+ const schema = {
254
+ validate: () => ({
255
+ error: new Error('validation failed'),
256
+ value: {},
257
+ }),
258
+ };
259
+ config_service_1.ConfigService.setOptions({
260
+ ignoreEnvVars: true,
261
+ ignoreEnvFile: true,
262
+ validationSchema: schema,
263
+ validationOptions: { abortEarly: true },
264
+ });
265
+ expect(() => new config_service_1.ConfigService()).not.toThrow();
266
+ });
267
+ });
268
+ });
269
+ describe('ConfigModule', () => {
270
+ beforeEach(() => {
271
+ config_service_1.ConfigService.setOptions({});
272
+ });
273
+ it('forRoot returns ConfigModule', () => {
274
+ const result = config_module_1.ConfigModule.forRoot();
275
+ expect(result).toBe(config_module_1.ConfigModule);
276
+ });
277
+ it('forRoot sets options on ConfigService', () => {
278
+ config_module_1.ConfigModule.forRoot({ ignoreEnvFile: true, ignoreEnvVars: true });
279
+ // If options were set, a new ConfigService should not try loading env files
280
+ mockedFs.existsSync.mockReturnValue(false);
281
+ expect(() => new config_service_1.ConfigService()).not.toThrow();
282
+ });
283
+ it('forRoot without options returns ConfigModule', () => {
284
+ const result = config_module_1.ConfigModule.forRoot();
285
+ expect(result).toBe(config_module_1.ConfigModule);
286
+ });
287
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hazeljs/config",
3
- "version": "0.2.0-beta.55",
3
+ "version": "0.2.0-beta.56",
4
4
  "description": "Configuration module for HazelJS framework",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -49,5 +49,5 @@
49
49
  "peerDependencies": {
50
50
  "@hazeljs/core": ">=0.2.0-beta.0"
51
51
  },
52
- "gitHead": "f2e54f346eea552595a44607999454a9e388cb9e"
52
+ "gitHead": "c2737e90974458a8438eee623726f0a453b66b8b"
53
53
  }