@geekmidas/envkit 0.0.2 → 0.0.3
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/dist/{EnvironmentParser-Dd6TbwJC.mjs → EnvironmentParser-Boj5EFmL.mjs} +53 -36
- package/dist/{EnvironmentParser-nZnZXM_Y.cjs → EnvironmentParser-Bz9IoLJs.cjs} +68 -36
- package/dist/EnvironmentParser.cjs +1 -1
- package/dist/EnvironmentParser.mjs +1 -1
- package/dist/__tests__/ConfigParser.spec.cjs +322 -0
- package/dist/__tests__/ConfigParser.spec.mjs +322 -0
- package/dist/__tests__/EnvironmentParser.spec.cjs +421 -0
- package/dist/__tests__/EnvironmentParser.spec.mjs +421 -0
- package/dist/index.cjs +1 -1
- package/dist/index.mjs +1 -1
- package/dist/magic-string.es-BrLHy2bw.cjs +1015 -0
- package/dist/magic-string.es-u4lFXMgd.mjs +1014 -0
- package/dist/vi.bdSIJ99Y-CT9xbG7X.cjs +14895 -0
- package/dist/vi.bdSIJ99Y-CkiV-M6Y.mjs +14903 -0
- package/package.json +1 -1
- package/src/EnvironmentParser.ts +87 -47
- package/src/__tests__/ConfigParser.spec.ts +394 -0
- package/src/__tests__/EnvironmentParser.spec.ts +692 -0
package/package.json
CHANGED
package/src/EnvironmentParser.ts
CHANGED
|
@@ -59,6 +59,73 @@ export class ConfigParser<TResponse extends EmptyObject> {
|
|
|
59
59
|
export class EnvironmentParser<T extends EmptyObject> {
|
|
60
60
|
constructor(private readonly config: T) {}
|
|
61
61
|
|
|
62
|
+
private wrapSchema = (schema: z.ZodType, name: string): z.ZodType => {
|
|
63
|
+
// Create a proxy that intercepts all method calls on the schema
|
|
64
|
+
return new Proxy(schema, {
|
|
65
|
+
get: (target, prop) => {
|
|
66
|
+
if (prop === 'parse') {
|
|
67
|
+
return () => {
|
|
68
|
+
const value = get(this.config, name);
|
|
69
|
+
try {
|
|
70
|
+
return target.parse(value);
|
|
71
|
+
} catch (error) {
|
|
72
|
+
if (error instanceof z.ZodError) {
|
|
73
|
+
// Modify the error to include the environment variable name
|
|
74
|
+
const modifiedIssues = error.issues.map((issue) => ({
|
|
75
|
+
...issue,
|
|
76
|
+
message: `Environment variable "${name}": ${issue.message}`,
|
|
77
|
+
path: [name, ...issue.path],
|
|
78
|
+
}));
|
|
79
|
+
throw new z.ZodError(modifiedIssues);
|
|
80
|
+
}
|
|
81
|
+
throw error;
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
if (prop === 'safeParse') {
|
|
87
|
+
return () => {
|
|
88
|
+
const value = get(this.config, name);
|
|
89
|
+
const result = target.safeParse(value);
|
|
90
|
+
|
|
91
|
+
if (!result.success) {
|
|
92
|
+
// Modify the error to include the environment variable name
|
|
93
|
+
const modifiedIssues = result.error.issues.map(
|
|
94
|
+
(issue: z.core.$ZodIssue) => ({
|
|
95
|
+
...issue,
|
|
96
|
+
message: `Environment variable "${name}": ${issue.message}`,
|
|
97
|
+
path: [name, ...issue.path],
|
|
98
|
+
}),
|
|
99
|
+
);
|
|
100
|
+
return {
|
|
101
|
+
success: false as const,
|
|
102
|
+
error: new z.ZodError(modifiedIssues),
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
return result;
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// For any method that returns a new schema (like transform, optional, etc.),
|
|
111
|
+
// wrap the result as well
|
|
112
|
+
const originalProp = target[prop as keyof typeof target];
|
|
113
|
+
if (typeof originalProp === 'function') {
|
|
114
|
+
return (...args: any[]) => {
|
|
115
|
+
const result = originalProp.apply(target, args);
|
|
116
|
+
// If the result is a ZodType, wrap it too
|
|
117
|
+
if (result && typeof result === 'object' && 'parse' in result) {
|
|
118
|
+
return this.wrapSchema(result, name);
|
|
119
|
+
}
|
|
120
|
+
return result;
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
return originalProp;
|
|
125
|
+
},
|
|
126
|
+
});
|
|
127
|
+
};
|
|
128
|
+
|
|
62
129
|
private getZodGetter = (name: string) => {
|
|
63
130
|
// Return an object that has all Zod schemas but with our wrapper
|
|
64
131
|
return new Proxy(
|
|
@@ -67,60 +134,33 @@ export class EnvironmentParser<T extends EmptyObject> {
|
|
|
67
134
|
get: (target, prop) => {
|
|
68
135
|
// deno-lint-ignore ban-ts-comment
|
|
69
136
|
// @ts-ignore
|
|
70
|
-
const
|
|
137
|
+
const value = target[prop];
|
|
71
138
|
|
|
72
|
-
if (typeof
|
|
139
|
+
if (typeof value === 'function') {
|
|
73
140
|
// Return a wrapper around each Zod schema creator
|
|
74
141
|
return (...args: any[]) => {
|
|
75
|
-
const schema =
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
schema.parse = () => {
|
|
81
|
-
const value = get(this.config, name);
|
|
82
|
-
try {
|
|
83
|
-
return originalParse.call(schema, value);
|
|
84
|
-
} catch (error) {
|
|
85
|
-
if (error instanceof z.ZodError) {
|
|
86
|
-
// Modify the error to include the environment variable name
|
|
87
|
-
const modifiedIssues = error.issues.map((issue) => ({
|
|
88
|
-
...issue,
|
|
89
|
-
message: `Environment variable "${name}": ${issue.message}`,
|
|
90
|
-
path: [name, ...issue.path],
|
|
91
|
-
}));
|
|
92
|
-
throw new z.ZodError(modifiedIssues);
|
|
93
|
-
}
|
|
94
|
-
throw error;
|
|
95
|
-
}
|
|
96
|
-
};
|
|
142
|
+
const schema = value(...args);
|
|
143
|
+
return this.wrapSchema(schema, name);
|
|
144
|
+
};
|
|
145
|
+
}
|
|
97
146
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
message: `Environment variable "${name}": ${issue.message}`,
|
|
108
|
-
path: [name, ...issue.path],
|
|
109
|
-
}),
|
|
110
|
-
);
|
|
111
|
-
return {
|
|
112
|
-
success: false as const,
|
|
113
|
-
error: new z.ZodError(modifiedIssues),
|
|
147
|
+
// Handle objects like z.coerce
|
|
148
|
+
if (value && typeof value === 'object') {
|
|
149
|
+
return new Proxy(value, {
|
|
150
|
+
get: (nestedTarget, nestedProp) => {
|
|
151
|
+
const nestedValue = nestedTarget[nestedProp as keyof typeof nestedTarget];
|
|
152
|
+
if (typeof nestedValue === 'function') {
|
|
153
|
+
return (...args: any[]) => {
|
|
154
|
+
const schema = nestedValue(...args);
|
|
155
|
+
return this.wrapSchema(schema, name);
|
|
114
156
|
};
|
|
115
157
|
}
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
return schema;
|
|
121
|
-
};
|
|
158
|
+
return nestedValue;
|
|
159
|
+
},
|
|
160
|
+
});
|
|
122
161
|
}
|
|
123
|
-
|
|
162
|
+
|
|
163
|
+
return value;
|
|
124
164
|
},
|
|
125
165
|
},
|
|
126
166
|
);
|
|
@@ -0,0 +1,394 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { z } from 'zod/v4';
|
|
3
|
+
import { ConfigParser } from '../EnvironmentParser';
|
|
4
|
+
|
|
5
|
+
describe('ConfigParser', () => {
|
|
6
|
+
describe('Basic functionality', () => {
|
|
7
|
+
it('should parse simple Zod schemas', () => {
|
|
8
|
+
const config = {
|
|
9
|
+
name: z.string().default('Test'),
|
|
10
|
+
age: z.number().default(25),
|
|
11
|
+
active: z.boolean().default(true),
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
const parser = new ConfigParser(config);
|
|
15
|
+
const result = parser.parse();
|
|
16
|
+
|
|
17
|
+
expect(result).toEqual({
|
|
18
|
+
name: 'Test',
|
|
19
|
+
age: 25,
|
|
20
|
+
active: true,
|
|
21
|
+
});
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it('should handle optional values', () => {
|
|
25
|
+
const config = {
|
|
26
|
+
required: z.string().default('value'),
|
|
27
|
+
optional: z.string().optional(),
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
const parser = new ConfigParser(config);
|
|
31
|
+
const result = parser.parse();
|
|
32
|
+
|
|
33
|
+
expect(result).toEqual({
|
|
34
|
+
required: 'value',
|
|
35
|
+
optional: undefined,
|
|
36
|
+
});
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it('should validate and use provided default values', () => {
|
|
40
|
+
const config = {
|
|
41
|
+
port: z.number().default(3000),
|
|
42
|
+
host: z.string().default('localhost'),
|
|
43
|
+
debug: z.boolean().default(false),
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
const parser = new ConfigParser(config);
|
|
47
|
+
const result = parser.parse();
|
|
48
|
+
|
|
49
|
+
expect(result).toEqual({
|
|
50
|
+
port: 3000,
|
|
51
|
+
host: 'localhost',
|
|
52
|
+
debug: false,
|
|
53
|
+
});
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
describe('Nested objects', () => {
|
|
58
|
+
it('should parse nested configuration objects', () => {
|
|
59
|
+
const config = {
|
|
60
|
+
database: {
|
|
61
|
+
host: z.string().default('localhost'),
|
|
62
|
+
port: z.number().default(5432),
|
|
63
|
+
ssl: z.boolean().default(false),
|
|
64
|
+
},
|
|
65
|
+
api: {
|
|
66
|
+
key: z.string().default('default-key'),
|
|
67
|
+
timeout: z.number().default(5000),
|
|
68
|
+
},
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
const parser = new ConfigParser(config);
|
|
72
|
+
const result = parser.parse();
|
|
73
|
+
|
|
74
|
+
expect(result).toEqual({
|
|
75
|
+
database: {
|
|
76
|
+
host: 'localhost',
|
|
77
|
+
port: 5432,
|
|
78
|
+
ssl: false,
|
|
79
|
+
},
|
|
80
|
+
api: {
|
|
81
|
+
key: 'default-key',
|
|
82
|
+
timeout: 5000,
|
|
83
|
+
},
|
|
84
|
+
});
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
it('should handle deeply nested objects', () => {
|
|
88
|
+
const config = {
|
|
89
|
+
app: {
|
|
90
|
+
name: z.string().default('MyApp'),
|
|
91
|
+
version: z.string().default('1.0.0'),
|
|
92
|
+
features: {
|
|
93
|
+
auth: {
|
|
94
|
+
enabled: z.boolean().default(true),
|
|
95
|
+
provider: z.string().default('local'),
|
|
96
|
+
},
|
|
97
|
+
cache: {
|
|
98
|
+
enabled: z.boolean().default(false),
|
|
99
|
+
ttl: z.number().default(3600),
|
|
100
|
+
},
|
|
101
|
+
},
|
|
102
|
+
},
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
const parser = new ConfigParser(config);
|
|
106
|
+
const result = parser.parse();
|
|
107
|
+
|
|
108
|
+
expect(result).toEqual({
|
|
109
|
+
app: {
|
|
110
|
+
name: 'MyApp',
|
|
111
|
+
version: '1.0.0',
|
|
112
|
+
features: {
|
|
113
|
+
auth: {
|
|
114
|
+
enabled: true,
|
|
115
|
+
provider: 'local',
|
|
116
|
+
},
|
|
117
|
+
cache: {
|
|
118
|
+
enabled: false,
|
|
119
|
+
ttl: 3600,
|
|
120
|
+
},
|
|
121
|
+
},
|
|
122
|
+
},
|
|
123
|
+
});
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
it('should handle mixed nested and flat configuration', () => {
|
|
127
|
+
const config = {
|
|
128
|
+
appName: z.string().default('Test App'),
|
|
129
|
+
database: {
|
|
130
|
+
url: z.string().default('postgres://localhost/test'),
|
|
131
|
+
poolSize: z.number().default(10),
|
|
132
|
+
},
|
|
133
|
+
port: z.number().default(3000),
|
|
134
|
+
features: {
|
|
135
|
+
logging: {
|
|
136
|
+
level: z.string().default('info'),
|
|
137
|
+
pretty: z.boolean().default(true),
|
|
138
|
+
},
|
|
139
|
+
},
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
const parser = new ConfigParser(config);
|
|
143
|
+
const result = parser.parse();
|
|
144
|
+
|
|
145
|
+
expect(result).toEqual({
|
|
146
|
+
appName: 'Test App',
|
|
147
|
+
database: {
|
|
148
|
+
url: 'postgres://localhost/test',
|
|
149
|
+
poolSize: 10,
|
|
150
|
+
},
|
|
151
|
+
port: 3000,
|
|
152
|
+
features: {
|
|
153
|
+
logging: {
|
|
154
|
+
level: 'info',
|
|
155
|
+
pretty: true,
|
|
156
|
+
},
|
|
157
|
+
},
|
|
158
|
+
});
|
|
159
|
+
});
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
describe('Error handling', () => {
|
|
163
|
+
it('should throw ZodError for schemas without defaults', () => {
|
|
164
|
+
const config = {
|
|
165
|
+
required: z.string(),
|
|
166
|
+
alsoRequired: z.number(),
|
|
167
|
+
};
|
|
168
|
+
|
|
169
|
+
const parser = new ConfigParser(config);
|
|
170
|
+
|
|
171
|
+
expect(() => parser.parse()).toThrow(z.ZodError);
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
it('should collect multiple validation errors', () => {
|
|
175
|
+
const config = {
|
|
176
|
+
field1: z.string(),
|
|
177
|
+
field2: z.number(),
|
|
178
|
+
field3: z.boolean(),
|
|
179
|
+
};
|
|
180
|
+
|
|
181
|
+
const parser = new ConfigParser(config);
|
|
182
|
+
|
|
183
|
+
try {
|
|
184
|
+
parser.parse();
|
|
185
|
+
// Should not reach here
|
|
186
|
+
expect(true).toBe(false);
|
|
187
|
+
} catch (error) {
|
|
188
|
+
expect(error).toBeInstanceOf(z.ZodError);
|
|
189
|
+
const zodError = error as z.ZodError;
|
|
190
|
+
expect(zodError.issues).toHaveLength(3);
|
|
191
|
+
}
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
it('should include correct paths in nested validation errors', () => {
|
|
195
|
+
const config = {
|
|
196
|
+
database: {
|
|
197
|
+
host: z.string(),
|
|
198
|
+
port: z.number(),
|
|
199
|
+
},
|
|
200
|
+
api: {
|
|
201
|
+
key: z.string(),
|
|
202
|
+
},
|
|
203
|
+
};
|
|
204
|
+
|
|
205
|
+
const parser = new ConfigParser(config);
|
|
206
|
+
|
|
207
|
+
try {
|
|
208
|
+
parser.parse();
|
|
209
|
+
// Should not reach here
|
|
210
|
+
expect(true).toBe(false);
|
|
211
|
+
} catch (error) {
|
|
212
|
+
expect(error).toBeInstanceOf(z.ZodError);
|
|
213
|
+
const zodError = error as z.ZodError;
|
|
214
|
+
|
|
215
|
+
const paths = zodError.issues.map((err) => err.path.join('.'));
|
|
216
|
+
expect(paths).toContain('database.host');
|
|
217
|
+
expect(paths).toContain('database.port');
|
|
218
|
+
expect(paths).toContain('api.key');
|
|
219
|
+
}
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
it('should use default values that pass validation', () => {
|
|
223
|
+
const config = {
|
|
224
|
+
port: z.number().min(1000).max(65535).default(3000),
|
|
225
|
+
email: z.string().email().default('admin@example.com'),
|
|
226
|
+
};
|
|
227
|
+
|
|
228
|
+
const parser = new ConfigParser(config);
|
|
229
|
+
const result = parser.parse();
|
|
230
|
+
|
|
231
|
+
expect(result).toEqual({
|
|
232
|
+
port: 3000,
|
|
233
|
+
email: 'admin@example.com',
|
|
234
|
+
});
|
|
235
|
+
});
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
describe('Type safety', () => {
|
|
239
|
+
it('should infer correct types for simple configuration', () => {
|
|
240
|
+
const config = {
|
|
241
|
+
name: z.string().default('test'),
|
|
242
|
+
count: z.number().default(42),
|
|
243
|
+
enabled: z.boolean().default(true),
|
|
244
|
+
};
|
|
245
|
+
|
|
246
|
+
const parser = new ConfigParser(config);
|
|
247
|
+
const result = parser.parse();
|
|
248
|
+
|
|
249
|
+
// TypeScript should infer the correct types
|
|
250
|
+
type ResultType = typeof result;
|
|
251
|
+
type ExpectedType = {
|
|
252
|
+
name: string;
|
|
253
|
+
count: number;
|
|
254
|
+
enabled: boolean;
|
|
255
|
+
};
|
|
256
|
+
|
|
257
|
+
const _typeCheck: ResultType extends ExpectedType ? true : false = true;
|
|
258
|
+
const _typeCheck2: ExpectedType extends ResultType ? true : false = true;
|
|
259
|
+
|
|
260
|
+
expect(_typeCheck).toBe(true);
|
|
261
|
+
expect(_typeCheck2).toBe(true);
|
|
262
|
+
});
|
|
263
|
+
|
|
264
|
+
it('should infer correct types for nested configuration', () => {
|
|
265
|
+
const config = {
|
|
266
|
+
database: {
|
|
267
|
+
host: z.string().default('localhost'),
|
|
268
|
+
port: z.number().default(5432),
|
|
269
|
+
},
|
|
270
|
+
features: {
|
|
271
|
+
auth: z.boolean().default(true),
|
|
272
|
+
},
|
|
273
|
+
};
|
|
274
|
+
|
|
275
|
+
const parser = new ConfigParser(config);
|
|
276
|
+
const result = parser.parse();
|
|
277
|
+
|
|
278
|
+
// TypeScript should infer the correct nested structure
|
|
279
|
+
type ResultType = typeof result;
|
|
280
|
+
type ExpectedType = {
|
|
281
|
+
database: { host: string; port: number };
|
|
282
|
+
features: { auth: boolean };
|
|
283
|
+
};
|
|
284
|
+
|
|
285
|
+
const _typeCheck: ResultType extends ExpectedType ? true : false = true;
|
|
286
|
+
const _typeCheck2: ExpectedType extends ResultType ? true : false = true;
|
|
287
|
+
|
|
288
|
+
expect(_typeCheck).toBe(true);
|
|
289
|
+
expect(_typeCheck2).toBe(true);
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
it('should handle optional types correctly', () => {
|
|
293
|
+
const config = {
|
|
294
|
+
required: z.string().default('value'),
|
|
295
|
+
optional: z.string().optional(),
|
|
296
|
+
nullable: z.string().nullable().default(null),
|
|
297
|
+
};
|
|
298
|
+
|
|
299
|
+
const parser = new ConfigParser(config);
|
|
300
|
+
const result = parser.parse();
|
|
301
|
+
});
|
|
302
|
+
});
|
|
303
|
+
|
|
304
|
+
describe('Complex schemas', () => {
|
|
305
|
+
it('should handle enum schemas', () => {
|
|
306
|
+
const config = {
|
|
307
|
+
environment: z
|
|
308
|
+
.enum(['development', 'staging', 'production'])
|
|
309
|
+
.default('development'),
|
|
310
|
+
logLevel: z.enum(['debug', 'info', 'warn', 'error']).default('info'),
|
|
311
|
+
};
|
|
312
|
+
|
|
313
|
+
const parser = new ConfigParser(config);
|
|
314
|
+
const result = parser.parse();
|
|
315
|
+
|
|
316
|
+
expect(result).toEqual({
|
|
317
|
+
environment: 'development',
|
|
318
|
+
logLevel: 'info',
|
|
319
|
+
});
|
|
320
|
+
});
|
|
321
|
+
|
|
322
|
+
it('should handle union schemas', () => {
|
|
323
|
+
const config = {
|
|
324
|
+
port: z.union([z.string(), z.number()]).default(3000),
|
|
325
|
+
timeout: z.union([z.number(), z.null()]).default(null),
|
|
326
|
+
};
|
|
327
|
+
|
|
328
|
+
const parser = new ConfigParser(config);
|
|
329
|
+
const result = parser.parse();
|
|
330
|
+
|
|
331
|
+
expect(result).toEqual({
|
|
332
|
+
port: 3000,
|
|
333
|
+
timeout: null,
|
|
334
|
+
});
|
|
335
|
+
});
|
|
336
|
+
|
|
337
|
+
it('should handle array schemas', () => {
|
|
338
|
+
const config = {
|
|
339
|
+
tags: z.array(z.string()).default(['tag1', 'tag2']),
|
|
340
|
+
ports: z.array(z.number()).default([3000, 3001]),
|
|
341
|
+
};
|
|
342
|
+
|
|
343
|
+
const parser = new ConfigParser(config);
|
|
344
|
+
const result = parser.parse();
|
|
345
|
+
|
|
346
|
+
expect(result).toEqual({
|
|
347
|
+
tags: ['tag1', 'tag2'],
|
|
348
|
+
ports: [3000, 3001],
|
|
349
|
+
});
|
|
350
|
+
});
|
|
351
|
+
|
|
352
|
+
it('should handle record schemas', () => {
|
|
353
|
+
const config = {
|
|
354
|
+
metadata: z
|
|
355
|
+
.record(z.string(), z.string())
|
|
356
|
+
.default({ key1: 'value1', key2: 'value2' }),
|
|
357
|
+
counters: z
|
|
358
|
+
.record(z.string(), z.number())
|
|
359
|
+
.default({ count1: 1, count2: 2 }),
|
|
360
|
+
};
|
|
361
|
+
|
|
362
|
+
const parser = new ConfigParser(config);
|
|
363
|
+
const result = parser.parse();
|
|
364
|
+
|
|
365
|
+
expect(result).toEqual({
|
|
366
|
+
metadata: { key1: 'value1', key2: 'value2' },
|
|
367
|
+
counters: { count1: 1, count2: 2 },
|
|
368
|
+
});
|
|
369
|
+
});
|
|
370
|
+
|
|
371
|
+
it('should handle transformed schemas', () => {
|
|
372
|
+
const config = {
|
|
373
|
+
portString: z.string().transform(Number).default(8080),
|
|
374
|
+
booleanString: z
|
|
375
|
+
.string()
|
|
376
|
+
.transform((v) => v === 'true')
|
|
377
|
+
.default(false),
|
|
378
|
+
jsonString: z
|
|
379
|
+
.string()
|
|
380
|
+
.transform((v) => JSON.parse(v))
|
|
381
|
+
.default({ key: 'value' }),
|
|
382
|
+
};
|
|
383
|
+
|
|
384
|
+
const parser = new ConfigParser(config);
|
|
385
|
+
const result = parser.parse();
|
|
386
|
+
|
|
387
|
+
expect(result).toEqual({
|
|
388
|
+
portString: 8080,
|
|
389
|
+
booleanString: false,
|
|
390
|
+
jsonString: { key: 'value' },
|
|
391
|
+
});
|
|
392
|
+
});
|
|
393
|
+
});
|
|
394
|
+
});
|