@proventuslabs/nestjs-zod 1.0.0-rc.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.
- package/README.md +297 -0
- package/dist/config/config.d.ts +56 -0
- package/dist/config/config.js +57 -0
- package/dist/config/config.js.map +1 -0
- package/dist/config/config.spec.d.ts +1 -0
- package/dist/config/config.spec.js +49 -0
- package/dist/config/config.spec.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +19 -0
- package/dist/index.js.map +1 -0
- package/dist/internal.d.ts +77 -0
- package/dist/internal.js +222 -0
- package/dist/internal.js.map +1 -0
- package/dist/module/module.d.ts +75 -0
- package/dist/module/module.js +94 -0
- package/dist/module/module.js.map +1 -0
- package/dist/module/module.spec.d.ts +1 -0
- package/dist/module/module.spec.js +186 -0
- package/dist/module/module.spec.js.map +1 -0
- package/package.json +66 -0
package/README.md
ADDED
|
@@ -0,0 +1,297 @@
|
|
|
1
|
+
# @proventuslabs/nestjs-zod
|
|
2
|
+
|
|
3
|
+
A collection of NestJS modules to integrate Zod into your application with enhanced configuration management and type safety.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- 🔧 **Zod-powered Configuration**: Type-safe configuration management using Zod schemas
|
|
8
|
+
- 🌍 **Environment Variable Support**: Automatic parsing and validation of environment variables
|
|
9
|
+
- 📁 **Configuration Files**: Support for YAML configuration files
|
|
10
|
+
- 🏗️ **Configurable Modules**: Enhanced NestJS configurable modules with Zod validation
|
|
11
|
+
- 🎯 **Type Safety**: Full TypeScript support with automatic type inference
|
|
12
|
+
- 🔄 **Merge Strategy**: Environment variables override configuration file values
|
|
13
|
+
- 📝 **Descriptive Errors**: Enhanced error messages with schema descriptions
|
|
14
|
+
|
|
15
|
+
## Installation
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
npm install @proventuslabs/nestjs-zod
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Peer Dependencies
|
|
22
|
+
|
|
23
|
+
This package requires the following peer dependencies:
|
|
24
|
+
|
|
25
|
+
- `@nestjs/common` ^10.0.0 || ^11.0.0
|
|
26
|
+
- `@nestjs/config` ^3.0.0 || ^4.0.0
|
|
27
|
+
- `zod` ^3.0.0
|
|
28
|
+
|
|
29
|
+
## Quick Start
|
|
30
|
+
|
|
31
|
+
### Configuration Management
|
|
32
|
+
|
|
33
|
+
```typescript
|
|
34
|
+
import { Module } from '@nestjs/common';
|
|
35
|
+
import { ConfigModule } from '@nestjs/config';
|
|
36
|
+
import { registerConfig, type ConfigType } from '@proventuslabs/nestjs-zod';
|
|
37
|
+
import { z } from 'zod';
|
|
38
|
+
|
|
39
|
+
// Define your configuration schema
|
|
40
|
+
const appConfig = registerConfig(
|
|
41
|
+
'app',
|
|
42
|
+
z.object({
|
|
43
|
+
port: z
|
|
44
|
+
.number()
|
|
45
|
+
.int()
|
|
46
|
+
.min(0)
|
|
47
|
+
.max(65535)
|
|
48
|
+
.default(3000)
|
|
49
|
+
.describe('The local HTTP port to bind the server to'),
|
|
50
|
+
host: z
|
|
51
|
+
.string()
|
|
52
|
+
.default('localhost')
|
|
53
|
+
.describe('The host to bind the server to'),
|
|
54
|
+
environment: z
|
|
55
|
+
.enum(['development', 'production', 'test'])
|
|
56
|
+
.default('development')
|
|
57
|
+
.describe('The application environment'),
|
|
58
|
+
}),
|
|
59
|
+
);
|
|
60
|
+
|
|
61
|
+
// Type inference
|
|
62
|
+
type AppConfig = ConfigType<typeof appConfig>;
|
|
63
|
+
|
|
64
|
+
@Module({
|
|
65
|
+
imports: [
|
|
66
|
+
ConfigModule.forRoot({
|
|
67
|
+
isGlobal: true,
|
|
68
|
+
load: [appConfig],
|
|
69
|
+
}),
|
|
70
|
+
],
|
|
71
|
+
})
|
|
72
|
+
export class AppModule {}
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### Using Configuration
|
|
76
|
+
|
|
77
|
+
```typescript
|
|
78
|
+
import { Injectable } from '@nestjs/common';
|
|
79
|
+
import { ConfigService } from '@nestjs/config';
|
|
80
|
+
import { type NamespacedConfigType } from '@proventuslabs/nestjs-zod';
|
|
81
|
+
|
|
82
|
+
@Injectable()
|
|
83
|
+
export class AppService {
|
|
84
|
+
constructor(
|
|
85
|
+
private configService: ConfigService<NamespacedConfigType<typeof appConfig>, true>
|
|
86
|
+
) {}
|
|
87
|
+
|
|
88
|
+
getPort(): number {
|
|
89
|
+
return this.configService.get('app.port', { infer: true });
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
getHost(): string {
|
|
93
|
+
return this.configService.get('app.host', { infer: true });
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
## Configuration Sources
|
|
99
|
+
|
|
100
|
+
The package supports multiple configuration sources with a clear precedence order:
|
|
101
|
+
|
|
102
|
+
1. **Environment Variables** (highest priority)
|
|
103
|
+
2. **Configuration Files** (YAML)
|
|
104
|
+
3. **Schema Defaults** (lowest priority)
|
|
105
|
+
|
|
106
|
+
### Environment Variables
|
|
107
|
+
|
|
108
|
+
Environment variables are automatically parsed and validated. Use the namespace prefix followed by underscores:
|
|
109
|
+
|
|
110
|
+
```bash
|
|
111
|
+
# For the 'app' namespace
|
|
112
|
+
APP_PORT=8080
|
|
113
|
+
APP_HOST=0.0.0.0
|
|
114
|
+
APP_ENVIRONMENT=production
|
|
115
|
+
|
|
116
|
+
# Nested configuration using double underscores
|
|
117
|
+
APP_DATABASE__HOST=localhost
|
|
118
|
+
APP_DATABASE__PORT=5432
|
|
119
|
+
APP_DATABASE__NAME=myapp
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
### Configuration Files
|
|
123
|
+
|
|
124
|
+
Support for YAML configuration files via environment variables:
|
|
125
|
+
|
|
126
|
+
```bash
|
|
127
|
+
# Specify a configuration file
|
|
128
|
+
CONFIG_FILE=./config/app.yaml
|
|
129
|
+
|
|
130
|
+
# Or provide inline YAML content
|
|
131
|
+
CONFIG_CONTENT="app:
|
|
132
|
+
port: 8080
|
|
133
|
+
host: 0.0.0.0
|
|
134
|
+
environment: production
|
|
135
|
+
database:
|
|
136
|
+
host: localhost
|
|
137
|
+
port: 5432
|
|
138
|
+
name: myapp"
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
Example `config/app.yaml`:
|
|
142
|
+
|
|
143
|
+
```yaml
|
|
144
|
+
app:
|
|
145
|
+
port: 8080
|
|
146
|
+
host: 0.0.0.0
|
|
147
|
+
environment: production
|
|
148
|
+
database:
|
|
149
|
+
host: localhost
|
|
150
|
+
port: 5432
|
|
151
|
+
name: myapp
|
|
152
|
+
redis:
|
|
153
|
+
host: localhost
|
|
154
|
+
port: 6379
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
## Configurable Modules
|
|
158
|
+
|
|
159
|
+
Create type-safe configurable modules with Zod validation:
|
|
160
|
+
|
|
161
|
+
```typescript
|
|
162
|
+
import { Module } from '@nestjs/common';
|
|
163
|
+
import { ZodConfigurableModuleBuilder } from '@proventuslabs/nestjs-zod';
|
|
164
|
+
import { z } from 'zod';
|
|
165
|
+
|
|
166
|
+
const { ConfigurableModuleClass, MODULE_OPTIONS_TOKEN } = new ZodConfigurableModuleBuilder(
|
|
167
|
+
z.object({
|
|
168
|
+
apiKey: z.string().min(1),
|
|
169
|
+
baseUrl: z.string().url().default('https://api.example.com'),
|
|
170
|
+
timeout: z.number().positive().default(5000),
|
|
171
|
+
})
|
|
172
|
+
).build();
|
|
173
|
+
|
|
174
|
+
@Module({
|
|
175
|
+
providers: [
|
|
176
|
+
{
|
|
177
|
+
provide: 'EXTERNAL_SERVICE',
|
|
178
|
+
useFactory: (options) => {
|
|
179
|
+
// options is fully typed and validated
|
|
180
|
+
return new ExternalService(options);
|
|
181
|
+
},
|
|
182
|
+
inject: [MODULE_OPTIONS_TOKEN],
|
|
183
|
+
},
|
|
184
|
+
],
|
|
185
|
+
exports: ['EXTERNAL_SERVICE'],
|
|
186
|
+
})
|
|
187
|
+
export class ExternalServiceModule extends ConfigurableModuleClass {}
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
### Using Configurable Modules
|
|
191
|
+
|
|
192
|
+
```typescript
|
|
193
|
+
// Static configuration
|
|
194
|
+
ExternalServiceModule.register({
|
|
195
|
+
apiKey: 'your-api-key',
|
|
196
|
+
baseUrl: 'https://custom-api.example.com',
|
|
197
|
+
timeout: 10000,
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
// Async configuration
|
|
201
|
+
ExternalServiceModule.registerAsync({
|
|
202
|
+
useFactory: (configService: ConfigService) => ({
|
|
203
|
+
apiKey: configService.get('EXTERNAL_API_KEY'),
|
|
204
|
+
baseUrl: configService.get('EXTERNAL_BASE_URL'),
|
|
205
|
+
timeout: configService.get('EXTERNAL_TIMEOUT'),
|
|
206
|
+
}),
|
|
207
|
+
inject: [ConfigService],
|
|
208
|
+
});
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
## API Reference
|
|
212
|
+
|
|
213
|
+
### `registerConfig`
|
|
214
|
+
|
|
215
|
+
Registers a configuration with the `ConfigModule.forFeature` for partial configuration under the provided namespace.
|
|
216
|
+
|
|
217
|
+
```typescript
|
|
218
|
+
function registerConfig<N extends string, C extends ConfigObject, I extends JsonValue>(
|
|
219
|
+
namespace: ConfigNamespace<N>,
|
|
220
|
+
configSchema: ZodType<C, ZodTypeDef, I>,
|
|
221
|
+
variables?: Record<string, string | undefined>
|
|
222
|
+
): ReturnType<typeof registerAs<C>> & { NAMESPACE: N }
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
**Parameters:**
|
|
226
|
+
- `namespace`: The namespace for the configuration (camelCase)
|
|
227
|
+
- `configSchema`: Zod schema for validation
|
|
228
|
+
- `variables`: Environment variables object (defaults to `process.env`)
|
|
229
|
+
|
|
230
|
+
**Returns:** A configuration provider that can be used with `ConfigModule.forRoot()`
|
|
231
|
+
|
|
232
|
+
### `ConfigType<T>`
|
|
233
|
+
|
|
234
|
+
Extracts the inferred type from a configuration schema.
|
|
235
|
+
|
|
236
|
+
```typescript
|
|
237
|
+
type ConfigType<T extends (...args: unknown[]) => unknown> = NestConfigType<T>;
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
### `NamespacedConfigType<T>`
|
|
241
|
+
|
|
242
|
+
Extracts the namespaced configuration type.
|
|
243
|
+
|
|
244
|
+
```typescript
|
|
245
|
+
type NamespacedConfigType<R> = {
|
|
246
|
+
[K in R["NAMESPACE"]]: NestConfigType<R>;
|
|
247
|
+
};
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
### `ZodConfigurableModuleBuilder`
|
|
251
|
+
|
|
252
|
+
Builder class for creating configurable modules with Zod validation.
|
|
253
|
+
|
|
254
|
+
```typescript
|
|
255
|
+
class ZodConfigurableModuleBuilder<
|
|
256
|
+
ModuleOptions,
|
|
257
|
+
ModuleOptionsInput = unknown,
|
|
258
|
+
StaticMethodKey extends string = string,
|
|
259
|
+
FactoryClassMethodKey extends string = string,
|
|
260
|
+
ExtraModuleDefinitionOptions = object
|
|
261
|
+
>
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
## Error Handling
|
|
265
|
+
|
|
266
|
+
The package provides enhanced error messages that include:
|
|
267
|
+
|
|
268
|
+
- Schema descriptions when available
|
|
269
|
+
- Original environment variable names
|
|
270
|
+
- Detailed validation error paths
|
|
271
|
+
- Helpful suggestions for fixing configuration issues
|
|
272
|
+
|
|
273
|
+
Example error output:
|
|
274
|
+
|
|
275
|
+
```
|
|
276
|
+
TypeError: Configuration validation failed for 'app' namespace
|
|
277
|
+
|
|
278
|
+
• app.port: Expected number, received string
|
|
279
|
+
Description: The local HTTP port to bind the server to
|
|
280
|
+
Environment Variable: APP_PORT
|
|
281
|
+
|
|
282
|
+
• app.database.host: Required
|
|
283
|
+
Description: Database host address
|
|
284
|
+
Environment Variable: APP_DATABASE__HOST
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
## Examples
|
|
288
|
+
|
|
289
|
+
See the [examples](./examples) directory for complete working examples.
|
|
290
|
+
|
|
291
|
+
## Contributing
|
|
292
|
+
|
|
293
|
+
Contributions are welcome! Please feel free to submit a Pull Request.
|
|
294
|
+
|
|
295
|
+
## License
|
|
296
|
+
|
|
297
|
+
This project is licensed under the MIT License - see the [LICENSE](../../LICENSE) file for details.
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { type ConfigObject, type ConfigType as NestConfigType } from "@nestjs/config";
|
|
2
|
+
import type { CamelCase, JsonValue } from "type-fest";
|
|
3
|
+
import { type ZodType, type ZodTypeDef } from "zod";
|
|
4
|
+
/**
|
|
5
|
+
* Simple type alias for config namespace.
|
|
6
|
+
*/
|
|
7
|
+
type ConfigNamespace<T extends string> = CamelCase<T>;
|
|
8
|
+
/**
|
|
9
|
+
* The flattened type of the config.
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* const config = registerConfig('app', z.object({ port: z.number() }));
|
|
13
|
+
* type AppConfig = ConfigType<typeof config>;
|
|
14
|
+
* // ^? { port: number }
|
|
15
|
+
*/
|
|
16
|
+
export type ConfigType<T extends (...args: unknown[]) => unknown> = NestConfigType<T>;
|
|
17
|
+
/**
|
|
18
|
+
* The type of the config for a given namespace.
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* const config = registerConfig('app', z.object({ port: z.number() }));
|
|
22
|
+
* type AppConfig = NamespacedConfigType<typeof config>;
|
|
23
|
+
* // ^? { app: { port: number } }
|
|
24
|
+
*/
|
|
25
|
+
export type NamespacedConfigType<R extends ReturnType<typeof registerConfig<string, any, any>>> = {
|
|
26
|
+
[K in R["NAMESPACE"]]: NestConfigType<R>;
|
|
27
|
+
};
|
|
28
|
+
/**
|
|
29
|
+
* Registers a config with the `ConfigModule.forFeature` for partial configuration under the provided namespace.
|
|
30
|
+
*
|
|
31
|
+
* This function handles configuration by validating and merging values from multiple sources:
|
|
32
|
+
* - **Environment Variables:** Reads environment variables as JSON values, using the namespace prefix and validating them against the provided schema.
|
|
33
|
+
* - **Configuration File:** Optionally reads configuration from a file or inline YAML content, specified by the `CONFIG_FILE` or `CONFIG_CONTENT` environment variables respectively.
|
|
34
|
+
*
|
|
35
|
+
* **Merge order is Config File < Enviornment Variables** thus enviornment variables _override_ whatever the config sets.
|
|
36
|
+
*
|
|
37
|
+
* @param namespace - The namespace of the config
|
|
38
|
+
* @param configSchema - The schema of the config
|
|
39
|
+
* @param variables - The environment variables - defaults to `process.env`
|
|
40
|
+
*
|
|
41
|
+
* @throws {TypeError} If the environment variables or configuration file content do not match the schema.
|
|
42
|
+
*
|
|
43
|
+
* @example
|
|
44
|
+
* const ConfigSchema = z.object({
|
|
45
|
+
* port: z.number().int().min(0).max(65535).default(9558).describe('The local HTTP port to bind the server to'),
|
|
46
|
+
* });
|
|
47
|
+
*
|
|
48
|
+
* export const appConfig = registerConfig('app', ConfigSchema);
|
|
49
|
+
*
|
|
50
|
+
* export type AppConfigNamespaced = NamespacedConfigType<typeof appConfig>;
|
|
51
|
+
* export type AppConfig = ConfigType<typeof appConfig>;
|
|
52
|
+
*/
|
|
53
|
+
export declare function registerConfig<N extends string, C extends ConfigObject, I extends JsonValue>(namespace: ConfigNamespace<N>, configSchema: ZodType<C, ZodTypeDef, I>, variables?: Record<string, string | undefined>): import("@nestjs/config").ConfigFactory<C> & import("@nestjs/config").ConfigFactoryKeyHost<C | Promise<C>> & {
|
|
54
|
+
NAMESPACE: N;
|
|
55
|
+
};
|
|
56
|
+
export default registerConfig;
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.registerConfig = registerConfig;
|
|
7
|
+
const node_process_1 = __importDefault(require("node:process"));
|
|
8
|
+
const config_1 = require("@nestjs/config");
|
|
9
|
+
const lodash_1 = require("lodash");
|
|
10
|
+
const zod_1 = require("zod");
|
|
11
|
+
const internal_1 = require("../internal");
|
|
12
|
+
/**
|
|
13
|
+
* Registers a config with the `ConfigModule.forFeature` for partial configuration under the provided namespace.
|
|
14
|
+
*
|
|
15
|
+
* This function handles configuration by validating and merging values from multiple sources:
|
|
16
|
+
* - **Environment Variables:** Reads environment variables as JSON values, using the namespace prefix and validating them against the provided schema.
|
|
17
|
+
* - **Configuration File:** Optionally reads configuration from a file or inline YAML content, specified by the `CONFIG_FILE` or `CONFIG_CONTENT` environment variables respectively.
|
|
18
|
+
*
|
|
19
|
+
* **Merge order is Config File < Enviornment Variables** thus enviornment variables _override_ whatever the config sets.
|
|
20
|
+
*
|
|
21
|
+
* @param namespace - The namespace of the config
|
|
22
|
+
* @param configSchema - The schema of the config
|
|
23
|
+
* @param variables - The environment variables - defaults to `process.env`
|
|
24
|
+
*
|
|
25
|
+
* @throws {TypeError} If the environment variables or configuration file content do not match the schema.
|
|
26
|
+
*
|
|
27
|
+
* @example
|
|
28
|
+
* const ConfigSchema = z.object({
|
|
29
|
+
* port: z.number().int().min(0).max(65535).default(9558).describe('The local HTTP port to bind the server to'),
|
|
30
|
+
* });
|
|
31
|
+
*
|
|
32
|
+
* export const appConfig = registerConfig('app', ConfigSchema);
|
|
33
|
+
*
|
|
34
|
+
* export type AppConfigNamespaced = NamespacedConfigType<typeof appConfig>;
|
|
35
|
+
* export type AppConfig = ConfigType<typeof appConfig>;
|
|
36
|
+
*/
|
|
37
|
+
function registerConfig(namespace, configSchema, variables = node_process_1.default.env) {
|
|
38
|
+
const service = (0, config_1.registerAs)(namespace, async () => {
|
|
39
|
+
const [decodedEnv, envKeys] = (0, internal_1.decodeVariables)(variables, namespace);
|
|
40
|
+
const decodedConfig = (0, internal_1.decodeConfig)(variables.CONFIG_CONTENT, variables.CONFIG_FILE);
|
|
41
|
+
const namespacedSchema = zod_1.z.object({
|
|
42
|
+
[namespace]: configSchema,
|
|
43
|
+
});
|
|
44
|
+
const parsedConfig = await namespacedSchema.safeParseAsync((0, lodash_1.merge)({ [namespace]: {} }, decodedConfig, decodedEnv));
|
|
45
|
+
if (!parsedConfig.success)
|
|
46
|
+
throw (0, internal_1.zodErrorToTypeError)(parsedConfig.error, namespacedSchema, namespace, envKeys);
|
|
47
|
+
const config = parsedConfig.data[namespace];
|
|
48
|
+
return config;
|
|
49
|
+
});
|
|
50
|
+
// we add the namespace to the object itself for runtime access too
|
|
51
|
+
return Object.defineProperty(service, "NAMESPACE", {
|
|
52
|
+
value: namespace,
|
|
53
|
+
writable: false,
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
exports.default = registerConfig;
|
|
57
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/config/config.ts"],"names":[],"mappings":";;;;;AAiEA,wCA2BC;AA5FD,gEAAmC;AAEnC,2CAAkG;AAElG,mCAA+B;AAE/B,6BAAuD;AAEvD,0CAAiF;AAgCjF;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,SAAgB,cAAc,CAC7B,SAA6B,EAC7B,YAAuC,EACvC,YAAgD,sBAAO,CAAC,GAAG;IAE3D,MAAM,OAAO,GAAG,IAAA,mBAAU,EAAC,SAAS,EAAE,KAAK,IAAI,EAAE;QAChD,MAAM,CAAC,UAAU,EAAE,OAAO,CAAC,GAAG,IAAA,0BAAe,EAAC,SAAS,EAAE,SAAS,CAAC,CAAC;QACpE,MAAM,aAAa,GAAG,IAAA,uBAAY,EAAC,SAAS,CAAC,cAAc,EAAE,SAAS,CAAC,WAAW,CAAC,CAAC;QAEpF,MAAM,gBAAgB,GAAG,OAAC,CAAC,MAAM,CAAC;YACjC,CAAC,SAAS,CAAC,EAAE,YAAY;SACzB,CAAC,CAAC;QACH,MAAM,YAAY,GAAG,MAAM,gBAAgB,CAAC,cAAc,CACzD,IAAA,cAAK,EAAC,EAAE,CAAC,SAAS,CAAC,EAAE,EAAE,EAAE,EAAE,aAAa,EAAE,UAAU,CAAC,CACrD,CAAC;QACF,IAAI,CAAC,YAAY,CAAC,OAAO;YACxB,MAAM,IAAA,8BAAmB,EAAC,YAAY,CAAC,KAAK,EAAE,gBAAgB,EAAE,SAAS,EAAE,OAAO,CAAC,CAAC;QACrF,MAAM,MAAM,GAAM,YAAY,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QAE/C,OAAO,MAAM,CAAC;IACf,CAAC,CAAwD,CAAC;IAE1D,mEAAmE;IACnE,OAAO,MAAM,CAAC,cAAc,CAAC,OAAO,EAAE,WAAW,EAAE;QAClD,KAAK,EAAE,SAAS;QAChB,QAAQ,EAAE,KAAK;KACf,CAAC,CAAC;AACJ,CAAC;AAED,kBAAe,cAAc,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const config_1 = require("@nestjs/config");
|
|
4
|
+
const testing_1 = require("@nestjs/testing");
|
|
5
|
+
const zod_1 = require("zod");
|
|
6
|
+
const config_2 = require("./config");
|
|
7
|
+
describe("config", () => {
|
|
8
|
+
it("should register config with ConfigModule.forFeature", async () => {
|
|
9
|
+
const config = (0, config_2.registerConfig)("app", zod_1.z.object({}), {});
|
|
10
|
+
const moduleRef = await testing_1.Test.createTestingModule({
|
|
11
|
+
imports: [config_1.ConfigModule.forFeature(config)],
|
|
12
|
+
}).compile();
|
|
13
|
+
const configService = moduleRef.get(config_1.ConfigService);
|
|
14
|
+
expect(configService).toBeDefined();
|
|
15
|
+
});
|
|
16
|
+
it("should return the correct config", async () => {
|
|
17
|
+
const config = (0, config_2.registerConfig)("app", zod_1.z.object({
|
|
18
|
+
port: zod_1.z.coerce
|
|
19
|
+
.number()
|
|
20
|
+
.positive()
|
|
21
|
+
.min(0)
|
|
22
|
+
.max(65535)
|
|
23
|
+
.default(9556)
|
|
24
|
+
.describe("The local HTTP port to bind the server to"),
|
|
25
|
+
}), {});
|
|
26
|
+
const moduleRef = await testing_1.Test.createTestingModule({
|
|
27
|
+
imports: [config_1.ConfigModule.forFeature(config)],
|
|
28
|
+
}).compile();
|
|
29
|
+
const configService = moduleRef.get(config_1.ConfigService);
|
|
30
|
+
expect(configService.get("app")).toEqual({
|
|
31
|
+
port: 9556,
|
|
32
|
+
});
|
|
33
|
+
});
|
|
34
|
+
it("should throw an error if the config is invalid", async () => {
|
|
35
|
+
const config = (0, config_2.registerConfig)("app", zod_1.z.object({
|
|
36
|
+
missing: zod_1.z.string().describe("This is a required field"),
|
|
37
|
+
}), {
|
|
38
|
+
APP_NOT_MISSING: "not missing",
|
|
39
|
+
});
|
|
40
|
+
const moduleRef = testing_1.Test.createTestingModule({
|
|
41
|
+
imports: [
|
|
42
|
+
config_1.ConfigModule.forRoot({ ignoreEnvFile: true, load: [] }),
|
|
43
|
+
config_1.ConfigModule.forFeature(config),
|
|
44
|
+
],
|
|
45
|
+
}).compile();
|
|
46
|
+
await expect(moduleRef).rejects.toThrow();
|
|
47
|
+
});
|
|
48
|
+
});
|
|
49
|
+
//# sourceMappingURL=config.spec.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.spec.js","sourceRoot":"","sources":["../../src/config/config.spec.ts"],"names":[],"mappings":";;AAAA,2CAA6D;AAC7D,6CAAuC;AAEvC,6BAAwB;AAExB,qCAA0C;AAE1C,QAAQ,CAAC,QAAQ,EAAE,GAAG,EAAE;IACvB,EAAE,CAAC,qDAAqD,EAAE,KAAK,IAAI,EAAE;QACpE,MAAM,MAAM,GAAG,IAAA,uBAAc,EAAC,KAAK,EAAE,OAAC,CAAC,MAAM,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC;QAEvD,MAAM,SAAS,GAAG,MAAM,cAAI,CAAC,mBAAmB,CAAC;YAChD,OAAO,EAAE,CAAC,qBAAY,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC;SAC1C,CAAC,CAAC,OAAO,EAAE,CAAC;QAEb,MAAM,aAAa,GAAG,SAAS,CAAC,GAAG,CAAC,sBAAa,CAAC,CAAC;QAEnD,MAAM,CAAC,aAAa,CAAC,CAAC,WAAW,EAAE,CAAC;IACrC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,kCAAkC,EAAE,KAAK,IAAI,EAAE;QACjD,MAAM,MAAM,GAAG,IAAA,uBAAc,EAC5B,KAAK,EACL,OAAC,CAAC,MAAM,CAAC;YACR,IAAI,EAAE,OAAC,CAAC,MAAM;iBACZ,MAAM,EAAE;iBACR,QAAQ,EAAE;iBACV,GAAG,CAAC,CAAC,CAAC;iBACN,GAAG,CAAC,KAAK,CAAC;iBACV,OAAO,CAAC,IAAI,CAAC;iBACb,QAAQ,CAAC,2CAA2C,CAAC;SACvD,CAAC,EACF,EAAE,CACF,CAAC;QAEF,MAAM,SAAS,GAAG,MAAM,cAAI,CAAC,mBAAmB,CAAC;YAChD,OAAO,EAAE,CAAC,qBAAY,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC;SAC1C,CAAC,CAAC,OAAO,EAAE,CAAC;QAEb,MAAM,aAAa,GAAG,SAAS,CAAC,GAAG,CAAC,sBAAa,CAAC,CAAC;QAEnD,MAAM,CAAC,aAAa,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC;YACxC,IAAI,EAAE,IAAI;SACV,CAAC,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,gDAAgD,EAAE,KAAK,IAAI,EAAE;QAC/D,MAAM,MAAM,GAAG,IAAA,uBAAc,EAC5B,KAAK,EACL,OAAC,CAAC,MAAM,CAAC;YACR,OAAO,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,0BAA0B,CAAC;SACxD,CAAC,EACF;YACC,eAAe,EAAE,aAAa;SAC9B,CACD,CAAC;QAEF,MAAM,SAAS,GAAG,cAAI,CAAC,mBAAmB,CAAC;YAC1C,OAAO,EAAE;gBACR,qBAAY,CAAC,OAAO,CAAC,EAAE,aAAa,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC;gBACvD,qBAAY,CAAC,UAAU,CAAC,MAAM,CAAC;aAC/B;SACD,CAAC,CAAC,OAAO,EAAE,CAAC;QAEb,MAAM,MAAM,CAAC,SAAS,CAAC,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC;IAC3C,CAAC,CAAC,CAAC;AACJ,CAAC,CAAC,CAAC"}
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
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 __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./config/config"), exports);
|
|
18
|
+
__exportStar(require("./module/module"), exports);
|
|
19
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,kDAAgC;AAChC,kDAAgC"}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import type { JsonValue } from "type-fest";
|
|
2
|
+
import { type ZodTypeAny, type z } from "zod";
|
|
3
|
+
/**
|
|
4
|
+
* Reads configuration from either a string content or a file path.
|
|
5
|
+
* String content takes precedence over file path to avoid unnecessary filesystem operations.
|
|
6
|
+
*
|
|
7
|
+
* @param content - Optional string content in YAML format
|
|
8
|
+
* @param file - Optional path to a configuration file
|
|
9
|
+
* @returns The parsed configuration as a record of string keys to JSON values
|
|
10
|
+
* @throws {Error} if neither content nor file contains a valid configuration object
|
|
11
|
+
*/
|
|
12
|
+
export declare function decodeConfig(content?: string, file?: string): Record<string, JsonValue>;
|
|
13
|
+
/**
|
|
14
|
+
* Transforms environment variables from a flat structure with a specific namespace prefix
|
|
15
|
+
* into a nested object structure with camelCase keys, while preserving the original keys
|
|
16
|
+
* for error reporting.
|
|
17
|
+
*
|
|
18
|
+
* For example, transforms:
|
|
19
|
+
* {
|
|
20
|
+
* "MY_APP_SERVER__HOST": "localhost",
|
|
21
|
+
* "MY_APP_DATABASE__PORT": "5432",
|
|
22
|
+
* "UNRELATED_VAR": "value"
|
|
23
|
+
* }
|
|
24
|
+
*
|
|
25
|
+
* With namespace "myApp" into:
|
|
26
|
+
* {
|
|
27
|
+
* "my_app": {
|
|
28
|
+
* "server": {
|
|
29
|
+
* "host": "localhost"
|
|
30
|
+
* },
|
|
31
|
+
* "database": {
|
|
32
|
+
* "port": 5432 // Note: jsonify attempts to parse values
|
|
33
|
+
* }
|
|
34
|
+
* }
|
|
35
|
+
* }
|
|
36
|
+
*
|
|
37
|
+
* @param variables - Record of environment variables with string keys and string values
|
|
38
|
+
* @param namespace - The namespace prefix to filter variables (will be converted to SNAKE_CASE)
|
|
39
|
+
* @returns A tuple containing:
|
|
40
|
+
* 1. The transformed nested configuration object
|
|
41
|
+
* 2. A Map relating the transformed path keys to original environment variable names for error reporting
|
|
42
|
+
*/
|
|
43
|
+
export declare function decodeVariables(variables: Record<string, string | undefined>, namespace: string): readonly [Record<string, JsonValue | undefined>, Map<string, string>];
|
|
44
|
+
/**
|
|
45
|
+
* Converts Zod validation errors into a more descriptive TypeError.
|
|
46
|
+
* Enhances error messages with schema descriptions when available.
|
|
47
|
+
*
|
|
48
|
+
* @param error - The Zod error containing validation issues
|
|
49
|
+
* @param schema - The namespaced schema that was used for validation
|
|
50
|
+
* @param namespace - The configuration namespace for context in error messages
|
|
51
|
+
* @param keys - Map of path strings to user-friendly key names for better error reporting
|
|
52
|
+
* @returns A TypeError with formatted error messages for each validation issue
|
|
53
|
+
*/
|
|
54
|
+
export declare function zodErrorToTypeError(error: z.ZodError, schema: ZodTypeAny, namespace: string, keys: Map<string, string>, isSchemaNamespaced?: boolean): ZodConfigError;
|
|
55
|
+
/**
|
|
56
|
+
* Simple error wrapper for expressing an invalid config from a zod schema.
|
|
57
|
+
*/
|
|
58
|
+
export declare class ZodConfigError extends TypeError {
|
|
59
|
+
readonly namespace: {
|
|
60
|
+
name: string;
|
|
61
|
+
description: string | undefined;
|
|
62
|
+
};
|
|
63
|
+
readonly configErrors: readonly {
|
|
64
|
+
path: string;
|
|
65
|
+
message: string;
|
|
66
|
+
description: string | undefined;
|
|
67
|
+
}[];
|
|
68
|
+
constructor(namespace: {
|
|
69
|
+
name: string;
|
|
70
|
+
description: string | undefined;
|
|
71
|
+
}, configErrors: readonly {
|
|
72
|
+
path: string;
|
|
73
|
+
message: string;
|
|
74
|
+
description: string | undefined;
|
|
75
|
+
}[], options?: ErrorOptions);
|
|
76
|
+
private static mapError;
|
|
77
|
+
}
|
package/dist/internal.js
ADDED
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.ZodConfigError = void 0;
|
|
7
|
+
exports.decodeConfig = decodeConfig;
|
|
8
|
+
exports.decodeVariables = decodeVariables;
|
|
9
|
+
exports.zodErrorToTypeError = zodErrorToTypeError;
|
|
10
|
+
const node_fs_1 = __importDefault(require("node:fs"));
|
|
11
|
+
const lodash_1 = require("lodash");
|
|
12
|
+
const yaml_1 = __importDefault(require("yaml"));
|
|
13
|
+
const zod_1 = require("zod");
|
|
14
|
+
/**
|
|
15
|
+
* Raw content of the config file as UTF-8.
|
|
16
|
+
*/
|
|
17
|
+
let cachedConfigFileContent;
|
|
18
|
+
/**
|
|
19
|
+
* Parsed YAML file - unknown as we don't know what shape it will have until we validate it.
|
|
20
|
+
*/
|
|
21
|
+
// eslint-disable-next-line @typescript-eslint/no-redundant-type-constituents
|
|
22
|
+
let parsedConfigFileContent;
|
|
23
|
+
/**
|
|
24
|
+
* Parses a string content into a JavaScript object using YAML parser.
|
|
25
|
+
* Uses a cached result if the content has been parsed before.
|
|
26
|
+
*
|
|
27
|
+
* @param content - The string content in YAML format to parse
|
|
28
|
+
* @returns The parsed JavaScript object representation of the YAML content
|
|
29
|
+
*/
|
|
30
|
+
function parseConfig(content) {
|
|
31
|
+
if (!(0, lodash_1.isUndefined)(parsedConfigFileContent))
|
|
32
|
+
return parsedConfigFileContent;
|
|
33
|
+
parsedConfigFileContent = yaml_1.default.parse(content);
|
|
34
|
+
return parsedConfigFileContent;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Reads and parses a configuration file from the filesystem.
|
|
38
|
+
* Uses cached file content if available to prevent multiple filesystem reads.
|
|
39
|
+
*
|
|
40
|
+
* @param path - The path to the configuration file
|
|
41
|
+
* @returns The parsed JavaScript object representation of the file
|
|
42
|
+
* @throws {Error} if the file cannot be read or parsed
|
|
43
|
+
*/
|
|
44
|
+
function parseConfigFile(path) {
|
|
45
|
+
try {
|
|
46
|
+
if (!(0, lodash_1.isString)(cachedConfigFileContent))
|
|
47
|
+
cachedConfigFileContent = node_fs_1.default.readFileSync(path).toString("utf8");
|
|
48
|
+
return parseConfig(cachedConfigFileContent);
|
|
49
|
+
}
|
|
50
|
+
catch (err) {
|
|
51
|
+
const message = err instanceof Error ? err.message : new String(err).toString();
|
|
52
|
+
throw new Error(`Unable to open the config file provided: ${message}`, {
|
|
53
|
+
cause: err,
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Reads configuration from either a string content or a file path.
|
|
59
|
+
* String content takes precedence over file path to avoid unnecessary filesystem operations.
|
|
60
|
+
*
|
|
61
|
+
* @param content - Optional string content in YAML format
|
|
62
|
+
* @param file - Optional path to a configuration file
|
|
63
|
+
* @returns The parsed configuration as a record of string keys to JSON values
|
|
64
|
+
* @throws {Error} if neither content nor file contains a valid configuration object
|
|
65
|
+
*/
|
|
66
|
+
function decodeConfig(content, file) {
|
|
67
|
+
let readConfig = {};
|
|
68
|
+
// NOTE: `content` takes precedence over a `file` (to avoid a file system read in case of both)
|
|
69
|
+
if ((0, lodash_1.isString)(content))
|
|
70
|
+
readConfig = parseConfig(content);
|
|
71
|
+
else if ((0, lodash_1.isString)(file))
|
|
72
|
+
readConfig = parseConfigFile(file);
|
|
73
|
+
if (!(0, lodash_1.isObjectLike)(readConfig))
|
|
74
|
+
throw new Error(`Config file provided must contain the JSON configuration object`);
|
|
75
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
|
76
|
+
return readConfig;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Transforms environment variable keys into nested object notation starting from the provided namespace.
|
|
80
|
+
* For example, transforms "APP_SERVER__HOST" into "app.server.host" using camelCase ("APP" was the env namespace).
|
|
81
|
+
*
|
|
82
|
+
* @param envKey - The environment variable key to transform
|
|
83
|
+
* @param envNamespace - The namespace prefix to replace with a dot notation
|
|
84
|
+
* @returns A string in dot notation following camelCase conventions
|
|
85
|
+
*/
|
|
86
|
+
function nestedConventionNamespaced(envKey, envNamespace) {
|
|
87
|
+
return envKey
|
|
88
|
+
.replace(`${envNamespace}_`, `${envNamespace}.`)
|
|
89
|
+
.toLowerCase()
|
|
90
|
+
.replace(/__/g, ".")
|
|
91
|
+
.replace(/[a-z_]+/g, (word) => (0, lodash_1.camelCase)(word));
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Attempts to parse a string value as JSON, falling back to the original string if parsing fails.
|
|
95
|
+
* Used for converting environment variables that might contain JSON values to mimic config file read from ENVs as well.
|
|
96
|
+
*
|
|
97
|
+
* @param value - The string value to parse as JSON
|
|
98
|
+
* @returns The parsed JSON value or the original string if parsing fails
|
|
99
|
+
*/
|
|
100
|
+
function jsonify(value) {
|
|
101
|
+
try {
|
|
102
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
|
103
|
+
return JSON.parse(value ?? "");
|
|
104
|
+
}
|
|
105
|
+
catch {
|
|
106
|
+
return value;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Transforms environment variables from a flat structure with a specific namespace prefix
|
|
111
|
+
* into a nested object structure with camelCase keys, while preserving the original keys
|
|
112
|
+
* for error reporting.
|
|
113
|
+
*
|
|
114
|
+
* For example, transforms:
|
|
115
|
+
* {
|
|
116
|
+
* "MY_APP_SERVER__HOST": "localhost",
|
|
117
|
+
* "MY_APP_DATABASE__PORT": "5432",
|
|
118
|
+
* "UNRELATED_VAR": "value"
|
|
119
|
+
* }
|
|
120
|
+
*
|
|
121
|
+
* With namespace "myApp" into:
|
|
122
|
+
* {
|
|
123
|
+
* "my_app": {
|
|
124
|
+
* "server": {
|
|
125
|
+
* "host": "localhost"
|
|
126
|
+
* },
|
|
127
|
+
* "database": {
|
|
128
|
+
* "port": 5432 // Note: jsonify attempts to parse values
|
|
129
|
+
* }
|
|
130
|
+
* }
|
|
131
|
+
* }
|
|
132
|
+
*
|
|
133
|
+
* @param variables - Record of environment variables with string keys and string values
|
|
134
|
+
* @param namespace - The namespace prefix to filter variables (will be converted to SNAKE_CASE)
|
|
135
|
+
* @returns A tuple containing:
|
|
136
|
+
* 1. The transformed nested configuration object
|
|
137
|
+
* 2. A Map relating the transformed path keys to original environment variable names for error reporting
|
|
138
|
+
*/
|
|
139
|
+
function decodeVariables(variables, namespace) {
|
|
140
|
+
const envKeys = new Map();
|
|
141
|
+
const envNamespace = (0, lodash_1.snakeCase)(namespace).toUpperCase();
|
|
142
|
+
const relevantEnv = (0, lodash_1.pickBy)(variables, (_value, key) => key.startsWith(envNamespace));
|
|
143
|
+
const decodedEnv = (0, lodash_1.reduce)(relevantEnv, (env, value, key) => {
|
|
144
|
+
const newKey = nestedConventionNamespaced(key, envNamespace);
|
|
145
|
+
envKeys.set(newKey, key);
|
|
146
|
+
const newValue = jsonify(value);
|
|
147
|
+
return (0, lodash_1.set)(env, newKey, newValue);
|
|
148
|
+
}, {});
|
|
149
|
+
return [decodedEnv, envKeys];
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Recursively navigates through a Zod schema to find description metadata at a specific path.
|
|
153
|
+
* Handles various Zod schema types including effects, transformers, and objects.
|
|
154
|
+
*
|
|
155
|
+
* @param path - Array of string keys representing the path in the schema
|
|
156
|
+
* @param schema - The Zod schema to search through
|
|
157
|
+
* @returns The description string if found, otherwise undefined
|
|
158
|
+
*/
|
|
159
|
+
function findDescriptionInSchemaByPath(path, schema) {
|
|
160
|
+
// we have to work with ZodTypeAny to accept anything, so we disable the unsafe argument check
|
|
161
|
+
if ((0, lodash_1.isEmpty)(path))
|
|
162
|
+
return schema?.description;
|
|
163
|
+
else if (schema instanceof zod_1.ZodEffects)
|
|
164
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
|
165
|
+
return findDescriptionInSchemaByPath(path, schema.innerType());
|
|
166
|
+
else if (schema instanceof zod_1.ZodTransformer)
|
|
167
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
|
168
|
+
return findDescriptionInSchemaByPath(path, schema.innerType());
|
|
169
|
+
else if (schema instanceof zod_1.ZodObject)
|
|
170
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
|
171
|
+
return findDescriptionInSchemaByPath((0, lodash_1.tail)(path), (0, lodash_1.get)(schema.shape, (0, lodash_1.head)(path) ?? ""));
|
|
172
|
+
else
|
|
173
|
+
return schema?.description;
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Converts Zod validation errors into a more descriptive TypeError.
|
|
177
|
+
* Enhances error messages with schema descriptions when available.
|
|
178
|
+
*
|
|
179
|
+
* @param error - The Zod error containing validation issues
|
|
180
|
+
* @param schema - The namespaced schema that was used for validation
|
|
181
|
+
* @param namespace - The configuration namespace for context in error messages
|
|
182
|
+
* @param keys - Map of path strings to user-friendly key names for better error reporting
|
|
183
|
+
* @returns A TypeError with formatted error messages for each validation issue
|
|
184
|
+
*/
|
|
185
|
+
function zodErrorToTypeError(error, schema, namespace, keys, isSchemaNamespaced = true) {
|
|
186
|
+
const errorMessages = error.issues.map((err) => {
|
|
187
|
+
const pathNotation = err.path.join(".");
|
|
188
|
+
const path = keys.get(pathNotation) ?? pathNotation;
|
|
189
|
+
const message = err.message;
|
|
190
|
+
const description = findDescriptionInSchemaByPath(err.path.map((v) => v.toString()), schema);
|
|
191
|
+
return { path, message, description };
|
|
192
|
+
});
|
|
193
|
+
return new ZodConfigError({
|
|
194
|
+
name: namespace,
|
|
195
|
+
description: findDescriptionInSchemaByPath(isSchemaNamespaced ? [namespace] : [], schema),
|
|
196
|
+
}, errorMessages);
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* Simple error wrapper for expressing an invalid config from a zod schema.
|
|
200
|
+
*/
|
|
201
|
+
class ZodConfigError extends TypeError {
|
|
202
|
+
namespace;
|
|
203
|
+
configErrors;
|
|
204
|
+
constructor(namespace, configErrors, options) {
|
|
205
|
+
const spacing = " ";
|
|
206
|
+
const title = `Invalid config for "${namespace.name}":\n`;
|
|
207
|
+
const description = (0, lodash_1.isUndefined)(namespace.description)
|
|
208
|
+
? ""
|
|
209
|
+
: `${spacing}${namespace.description}\n`;
|
|
210
|
+
const errors = configErrors.map((v) => ZodConfigError.mapError(v, spacing)).join("\n");
|
|
211
|
+
super(title + description + errors, options);
|
|
212
|
+
this.namespace = namespace;
|
|
213
|
+
this.configErrors = configErrors;
|
|
214
|
+
}
|
|
215
|
+
static mapError(error, spacing) {
|
|
216
|
+
let message = `${spacing}- ${error.path}: ${error.message}`;
|
|
217
|
+
message += (0, lodash_1.isUndefined)(error.description) ? "" : `\n${spacing}${error.description}`;
|
|
218
|
+
return message;
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
exports.ZodConfigError = ZodConfigError;
|
|
222
|
+
//# sourceMappingURL=internal.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"internal.js","sourceRoot":"","sources":["../src/internal.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { type ConfigurableModuleAsyncOptions, ConfigurableModuleBuilder, type ConfigurableModuleBuilderOptions, type DynamicModule } from "@nestjs/common";
|
|
2
|
+
import type { DEFAULT_FACTORY_CLASS_METHOD_KEY, DEFAULT_METHOD_KEY } from "@nestjs/common/module-utils/constants";
|
|
3
|
+
import { type ZodType, type ZodTypeDef } from "zod";
|
|
4
|
+
export declare class ZodConfigurableModuleBuilder<ModuleOptions, ModuleOptionsInput = unknown, StaticMethodKey extends string = typeof DEFAULT_METHOD_KEY, FactoryClassMethodKey extends string = typeof DEFAULT_FACTORY_CLASS_METHOD_KEY, ExtraModuleDefinitionOptions = object> {
|
|
5
|
+
protected readonly schema: ZodType<ModuleOptions, ZodTypeDef, ModuleOptionsInput>;
|
|
6
|
+
protected readonly options: ConfigurableModuleBuilderOptions;
|
|
7
|
+
protected base: ConfigurableModuleBuilder<ModuleOptionsInput, StaticMethodKey, FactoryClassMethodKey, ExtraModuleDefinitionOptions>;
|
|
8
|
+
private transformSet;
|
|
9
|
+
constructor(schema: ZodType<ModuleOptions, ZodTypeDef, ModuleOptionsInput>, options?: ConfigurableModuleBuilderOptions, parentBuilder?: ConfigurableModuleBuilder<ModuleOptionsInput>);
|
|
10
|
+
setExtras<ExtraModuleDefinitionOptions>(extras: ExtraModuleDefinitionOptions, transformDefinition?: (definition: DynamicModule, extras: ExtraModuleDefinitionOptions) => DynamicModule): ZodConfigurableModuleBuilder<ModuleOptions, ModuleOptionsInput, StaticMethodKey, FactoryClassMethodKey, ExtraModuleDefinitionOptions>;
|
|
11
|
+
private patchTransform;
|
|
12
|
+
setClassMethodName<StaticMethodKey extends string>(key: StaticMethodKey): ZodConfigurableModuleBuilder<ModuleOptions, ModuleOptionsInput, StaticMethodKey, FactoryClassMethodKey, ExtraModuleDefinitionOptions>;
|
|
13
|
+
setFactoryMethodName<FactoryClassMethodKey extends string>(key: FactoryClassMethodKey): ZodConfigurableModuleBuilder<ModuleOptions, ModuleOptionsInput, StaticMethodKey, FactoryClassMethodKey, ExtraModuleDefinitionOptions>;
|
|
14
|
+
build(): ZodConfigurableModuleHost<ModuleOptions, ModuleOptionsInput, StaticMethodKey, FactoryClassMethodKey, ExtraModuleDefinitionOptions>;
|
|
15
|
+
private transformModuleDefinitionWithSchema;
|
|
16
|
+
}
|
|
17
|
+
type ZodConfigurableModuleCls<ModuleOptions, ModuleOptionsInput = unknown, MethodKey extends string = typeof DEFAULT_METHOD_KEY, FactoryClassMethodKey extends string = typeof DEFAULT_FACTORY_CLASS_METHOD_KEY, ExtraModuleDefinitionOptions = object> = {
|
|
18
|
+
new (): any;
|
|
19
|
+
} & Record<`${MethodKey}`, (options: ModuleOptionsInput & Partial<ExtraModuleDefinitionOptions>) => DynamicModule> & Record<`${MethodKey}Async`, (options: ConfigurableModuleAsyncOptions<ModuleOptionsInput, FactoryClassMethodKey> & Partial<ExtraModuleDefinitionOptions>) => DynamicModule>;
|
|
20
|
+
interface ZodConfigurableModuleHost<ModuleOptions = Record<string, unknown>, ModuleOptionsInput = unknown, MethodKey extends string = string, FactoryClassMethodKey extends string = string, ExtraModuleDefinitionOptions = object> {
|
|
21
|
+
/**
|
|
22
|
+
* Class that represents a blueprint/prototype for a configurable Nest module.
|
|
23
|
+
* This class provides static methods for constructing dynamic modules. Their names
|
|
24
|
+
* can be controlled through the "MethodKey" type argument.
|
|
25
|
+
*
|
|
26
|
+
* Your module class should inherit from this class to make the static methods available.
|
|
27
|
+
*
|
|
28
|
+
* @example
|
|
29
|
+
* ```typescript
|
|
30
|
+
* @Module({})
|
|
31
|
+
* class IntegrationModule extends ConfigurableModuleCls {
|
|
32
|
+
* // ...
|
|
33
|
+
* }
|
|
34
|
+
* ```
|
|
35
|
+
*/
|
|
36
|
+
ConfigurableModuleClass: ZodConfigurableModuleCls<ModuleOptions, ModuleOptionsInput, MethodKey, FactoryClassMethodKey, ExtraModuleDefinitionOptions>;
|
|
37
|
+
/**
|
|
38
|
+
* Module options provider token. Can be used to inject the "options object" to
|
|
39
|
+
* providers registered within the host module.
|
|
40
|
+
*/
|
|
41
|
+
MODULE_OPTIONS_TOKEN: string | symbol;
|
|
42
|
+
/**
|
|
43
|
+
* Can be used to auto-infer the compound "async module options" type.
|
|
44
|
+
* Note: this property is not supposed to be used as a value.
|
|
45
|
+
*
|
|
46
|
+
* @example
|
|
47
|
+
* ```typescript
|
|
48
|
+
* @Module({})
|
|
49
|
+
* class IntegrationModule extends ConfigurableModuleCls {
|
|
50
|
+
* static module = initializer(IntegrationModule);
|
|
51
|
+
*
|
|
52
|
+
* static registerAsync(options: typeof ASYNC_OPTIONS_TYPE): DynamicModule {
|
|
53
|
+
* return super.registerAsync(options);
|
|
54
|
+
* }
|
|
55
|
+
* ```
|
|
56
|
+
*/
|
|
57
|
+
OPTIONS_TYPE: ModuleOptions & Partial<ExtraModuleDefinitionOptions>;
|
|
58
|
+
/**
|
|
59
|
+
* Can be used to auto-infer the compound "module options" type (options interface + extra module definition options).
|
|
60
|
+
* Note: this property is not supposed to be used as a value.
|
|
61
|
+
*
|
|
62
|
+
* @example
|
|
63
|
+
* ```typescript
|
|
64
|
+
* @Module({})
|
|
65
|
+
* class IntegrationModule extends ConfigurableModuleCls {
|
|
66
|
+
* static module = initializer(IntegrationModule);
|
|
67
|
+
*
|
|
68
|
+
* static register(options: typeof OPTIONS_TYPE): DynamicModule {
|
|
69
|
+
* return super.register(options);
|
|
70
|
+
* }
|
|
71
|
+
* ```
|
|
72
|
+
*/
|
|
73
|
+
ASYNC_OPTIONS_TYPE: ConfigurableModuleAsyncOptions<ModuleOptions, FactoryClassMethodKey> & Partial<ExtraModuleDefinitionOptions>;
|
|
74
|
+
}
|
|
75
|
+
export {};
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ZodConfigurableModuleBuilder = void 0;
|
|
4
|
+
const common_1 = require("@nestjs/common");
|
|
5
|
+
const lodash_1 = require("lodash");
|
|
6
|
+
const zod_1 = require("zod");
|
|
7
|
+
const internal_1 = require("../internal");
|
|
8
|
+
class ZodConfigurableModuleBuilder {
|
|
9
|
+
schema;
|
|
10
|
+
options;
|
|
11
|
+
base;
|
|
12
|
+
transformSet = false;
|
|
13
|
+
constructor(schema, options = {}, parentBuilder) {
|
|
14
|
+
this.schema = schema;
|
|
15
|
+
this.options = options;
|
|
16
|
+
this.base = new common_1.ConfigurableModuleBuilder(options, parentBuilder);
|
|
17
|
+
}
|
|
18
|
+
setExtras(extras, transformDefinition = (def) => def) {
|
|
19
|
+
const builder = new ZodConfigurableModuleBuilder(this.schema, this.options, this);
|
|
20
|
+
// this was called and we patched
|
|
21
|
+
this.transformSet = true;
|
|
22
|
+
builder.base = this.patchTransform(extras, transformDefinition);
|
|
23
|
+
return builder;
|
|
24
|
+
}
|
|
25
|
+
patchTransform(extras, transformDefinition = (def) => def) {
|
|
26
|
+
transformDefinition ??= (definition) => definition;
|
|
27
|
+
const existingTransform = transformDefinition;
|
|
28
|
+
transformDefinition = (definition, extraOptions) => {
|
|
29
|
+
const result = existingTransform(definition, extraOptions);
|
|
30
|
+
return this.transformModuleDefinitionWithSchema(result, extraOptions, extras);
|
|
31
|
+
};
|
|
32
|
+
return this.base.setExtras(extras, transformDefinition);
|
|
33
|
+
}
|
|
34
|
+
setClassMethodName(key) {
|
|
35
|
+
const builder = new ZodConfigurableModuleBuilder(this.schema, this.options, this);
|
|
36
|
+
builder.base = builder.base.setClassMethodName(key);
|
|
37
|
+
return builder;
|
|
38
|
+
}
|
|
39
|
+
setFactoryMethodName(key) {
|
|
40
|
+
const builder = new ZodConfigurableModuleBuilder(this.schema, this.options, this);
|
|
41
|
+
builder.base = builder.base.setFactoryMethodName(key);
|
|
42
|
+
return builder;
|
|
43
|
+
}
|
|
44
|
+
build() {
|
|
45
|
+
if (!this.transformSet)
|
|
46
|
+
this.base = this.patchTransform({});
|
|
47
|
+
return this.base.build();
|
|
48
|
+
}
|
|
49
|
+
transformModuleDefinitionWithSchema(definition, extraOptions, extras) {
|
|
50
|
+
const isOptionProvider = (provider) => "provide" in provider && provider.provide === this.options.optionsInjectionToken;
|
|
51
|
+
definition.providers = definition.providers?.map((provider) => {
|
|
52
|
+
if (isOptionProvider(provider)) {
|
|
53
|
+
if ("useFactory" in provider) {
|
|
54
|
+
const existingFactory = provider.useFactory;
|
|
55
|
+
provider.useFactory = async (...args) => {
|
|
56
|
+
try {
|
|
57
|
+
return await this.schema.parseAsync(existingFactory(...args));
|
|
58
|
+
}
|
|
59
|
+
catch (err) {
|
|
60
|
+
if (err instanceof zod_1.ZodError) {
|
|
61
|
+
throw (0, internal_1.zodErrorToTypeError)(err, this.schema, definition.module.name, new Map(), false);
|
|
62
|
+
}
|
|
63
|
+
throw err;
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
return {
|
|
69
|
+
// biome-ignore lint/style/noNonNullAssertion: the token is guaranteed to be set
|
|
70
|
+
provide: this.options.optionsInjectionToken,
|
|
71
|
+
useFactory: async () => {
|
|
72
|
+
const finalOptions = (0, lodash_1.isObject)(extraOptions)
|
|
73
|
+
? (0, lodash_1.omit)(extraOptions, (0, lodash_1.keys)(extras))
|
|
74
|
+
: extraOptions;
|
|
75
|
+
try {
|
|
76
|
+
return await this.schema.parseAsync(finalOptions);
|
|
77
|
+
}
|
|
78
|
+
catch (err) {
|
|
79
|
+
if (err instanceof zod_1.ZodError) {
|
|
80
|
+
throw (0, internal_1.zodErrorToTypeError)(err, this.schema, definition.module.name, new Map(), false);
|
|
81
|
+
}
|
|
82
|
+
throw err;
|
|
83
|
+
}
|
|
84
|
+
},
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
return provider;
|
|
89
|
+
});
|
|
90
|
+
return definition;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
exports.ZodConfigurableModuleBuilder = ZodConfigurableModuleBuilder;
|
|
94
|
+
//# sourceMappingURL=module.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"module.js","sourceRoot":"","sources":["../../src/module/module.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __esDecorate = (this && this.__esDecorate) || function (ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) {
|
|
3
|
+
function accept(f) { if (f !== void 0 && typeof f !== "function") throw new TypeError("Function expected"); return f; }
|
|
4
|
+
var kind = contextIn.kind, key = kind === "getter" ? "get" : kind === "setter" ? "set" : "value";
|
|
5
|
+
var target = !descriptorIn && ctor ? contextIn["static"] ? ctor : ctor.prototype : null;
|
|
6
|
+
var descriptor = descriptorIn || (target ? Object.getOwnPropertyDescriptor(target, contextIn.name) : {});
|
|
7
|
+
var _, done = false;
|
|
8
|
+
for (var i = decorators.length - 1; i >= 0; i--) {
|
|
9
|
+
var context = {};
|
|
10
|
+
for (var p in contextIn) context[p] = p === "access" ? {} : contextIn[p];
|
|
11
|
+
for (var p in contextIn.access) context.access[p] = contextIn.access[p];
|
|
12
|
+
context.addInitializer = function (f) { if (done) throw new TypeError("Cannot add initializers after decoration has completed"); extraInitializers.push(accept(f || null)); };
|
|
13
|
+
var result = (0, decorators[i])(kind === "accessor" ? { get: descriptor.get, set: descriptor.set } : descriptor[key], context);
|
|
14
|
+
if (kind === "accessor") {
|
|
15
|
+
if (result === void 0) continue;
|
|
16
|
+
if (result === null || typeof result !== "object") throw new TypeError("Object expected");
|
|
17
|
+
if (_ = accept(result.get)) descriptor.get = _;
|
|
18
|
+
if (_ = accept(result.set)) descriptor.set = _;
|
|
19
|
+
if (_ = accept(result.init)) initializers.unshift(_);
|
|
20
|
+
}
|
|
21
|
+
else if (_ = accept(result)) {
|
|
22
|
+
if (kind === "field") initializers.unshift(_);
|
|
23
|
+
else descriptor[key] = _;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
if (target) Object.defineProperty(target, contextIn.name, descriptor);
|
|
27
|
+
done = true;
|
|
28
|
+
};
|
|
29
|
+
var __runInitializers = (this && this.__runInitializers) || function (thisArg, initializers, value) {
|
|
30
|
+
var useValue = arguments.length > 2;
|
|
31
|
+
for (var i = 0; i < initializers.length; i++) {
|
|
32
|
+
value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg);
|
|
33
|
+
}
|
|
34
|
+
return useValue ? value : void 0;
|
|
35
|
+
};
|
|
36
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
37
|
+
const common_1 = require("@nestjs/common");
|
|
38
|
+
const testing_1 = require("@nestjs/testing");
|
|
39
|
+
const zod_1 = require("zod");
|
|
40
|
+
const module_1 = require("./module");
|
|
41
|
+
describe("module", () => {
|
|
42
|
+
const MyOptionSchema = zod_1.z
|
|
43
|
+
.object({
|
|
44
|
+
useFlags: zod_1.z.boolean().describe("Whether the use of flag is considered when connecting"),
|
|
45
|
+
clientName: zod_1.z.string().min(5).describe("The client name used for connecting to the universe"),
|
|
46
|
+
})
|
|
47
|
+
.describe("The options for connecting using the dynamic module")
|
|
48
|
+
.transform((v) => ({ ...v, supportsTransforms: false }))
|
|
49
|
+
.transform(async (v) => ({ ...v, supportsTransforms: true }));
|
|
50
|
+
const { ConfigurableModuleClass, MODULE_OPTIONS_TOKEN } = new module_1.ZodConfigurableModuleBuilder(MyOptionSchema)
|
|
51
|
+
.setExtras({
|
|
52
|
+
isGlobal: false,
|
|
53
|
+
}, (definition, extras) => ({
|
|
54
|
+
...definition,
|
|
55
|
+
global: extras.isGlobal,
|
|
56
|
+
}))
|
|
57
|
+
.setClassMethodName("forRoot")
|
|
58
|
+
.build();
|
|
59
|
+
let DynModule = (() => {
|
|
60
|
+
let _classDecorators = [(0, common_1.Module)({})];
|
|
61
|
+
let _classDescriptor;
|
|
62
|
+
let _classExtraInitializers = [];
|
|
63
|
+
let _classThis;
|
|
64
|
+
let _classSuper = ConfigurableModuleClass;
|
|
65
|
+
var DynModule = class extends _classSuper {
|
|
66
|
+
static { _classThis = this; }
|
|
67
|
+
static {
|
|
68
|
+
const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0;
|
|
69
|
+
__esDecorate(null, _classDescriptor = { value: _classThis }, _classDecorators, { kind: "class", name: _classThis.name, metadata: _metadata }, null, _classExtraInitializers);
|
|
70
|
+
DynModule = _classThis = _classDescriptor.value;
|
|
71
|
+
if (_metadata) Object.defineProperty(_classThis, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
|
|
72
|
+
__runInitializers(_classThis, _classExtraInitializers);
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
return DynModule = _classThis;
|
|
76
|
+
})();
|
|
77
|
+
it("should register correct options with static synchronous method", async () => {
|
|
78
|
+
const moduleRef = await testing_1.Test.createTestingModule({
|
|
79
|
+
imports: [
|
|
80
|
+
DynModule.forRoot({
|
|
81
|
+
useFlags: true,
|
|
82
|
+
clientName: "some long enough client name",
|
|
83
|
+
}),
|
|
84
|
+
],
|
|
85
|
+
}).compile();
|
|
86
|
+
const options = moduleRef.get(MODULE_OPTIONS_TOKEN);
|
|
87
|
+
expect(options).toMatchObject({
|
|
88
|
+
useFlags: true,
|
|
89
|
+
clientName: "some long enough client name",
|
|
90
|
+
supportsTransforms: true,
|
|
91
|
+
});
|
|
92
|
+
});
|
|
93
|
+
it("should register correct options with asynchronous method", async () => {
|
|
94
|
+
const DEFAULTS_TOKEN = "DEFAULT_OPTIONS";
|
|
95
|
+
const defaultsValue = {
|
|
96
|
+
useFlags: false,
|
|
97
|
+
};
|
|
98
|
+
const moduleRef = await testing_1.Test.createTestingModule({
|
|
99
|
+
imports: [
|
|
100
|
+
DynModule.forRootAsync({
|
|
101
|
+
provideInjectionTokensFrom: [{ provide: DEFAULTS_TOKEN, useValue: defaultsValue }],
|
|
102
|
+
useFactory: (defaults) => {
|
|
103
|
+
return {
|
|
104
|
+
...defaults,
|
|
105
|
+
clientName: "some long enough client name",
|
|
106
|
+
};
|
|
107
|
+
},
|
|
108
|
+
inject: [{ token: DEFAULTS_TOKEN, optional: false }],
|
|
109
|
+
}),
|
|
110
|
+
],
|
|
111
|
+
}).compile();
|
|
112
|
+
const options = moduleRef.get(MODULE_OPTIONS_TOKEN);
|
|
113
|
+
expect(options).toMatchObject({
|
|
114
|
+
useFlags: false,
|
|
115
|
+
clientName: "some long enough client name",
|
|
116
|
+
supportsTransforms: true,
|
|
117
|
+
});
|
|
118
|
+
});
|
|
119
|
+
it("should throw an error when clientName is too short", async () => {
|
|
120
|
+
await expect(testing_1.Test.createTestingModule({
|
|
121
|
+
imports: [
|
|
122
|
+
DynModule.forRoot({
|
|
123
|
+
useFlags: true,
|
|
124
|
+
clientName: "shor", // Less than 5 characters
|
|
125
|
+
}),
|
|
126
|
+
],
|
|
127
|
+
}).compile()).rejects.toThrow();
|
|
128
|
+
});
|
|
129
|
+
it("should throw an error when required options are missing", async () => {
|
|
130
|
+
await expect(testing_1.Test.createTestingModule({
|
|
131
|
+
imports: [
|
|
132
|
+
// @ts-expect-error: we are passing what could be a complete wrong and unchecked object
|
|
133
|
+
DynModule.forRoot({
|
|
134
|
+
// Missing clientName
|
|
135
|
+
useFlags: true,
|
|
136
|
+
}),
|
|
137
|
+
],
|
|
138
|
+
}).compile()).rejects.toThrow();
|
|
139
|
+
});
|
|
140
|
+
it("should throw an error when async factory returns invalid options", async () => {
|
|
141
|
+
const DEFAULTS_TOKEN = "INVALID_DEFAULTS";
|
|
142
|
+
const defaultsValue = {
|
|
143
|
+
useFlags: false,
|
|
144
|
+
};
|
|
145
|
+
await expect(testing_1.Test.createTestingModule({
|
|
146
|
+
imports: [
|
|
147
|
+
DynModule.forRootAsync({
|
|
148
|
+
provideInjectionTokensFrom: [{ provide: DEFAULTS_TOKEN, useValue: defaultsValue }],
|
|
149
|
+
useFactory: (defaults) => {
|
|
150
|
+
return {
|
|
151
|
+
...defaults,
|
|
152
|
+
clientName: "bad", // Too short
|
|
153
|
+
};
|
|
154
|
+
},
|
|
155
|
+
inject: [{ token: DEFAULTS_TOKEN, optional: false }],
|
|
156
|
+
}),
|
|
157
|
+
],
|
|
158
|
+
}).compile()).rejects.toThrow();
|
|
159
|
+
});
|
|
160
|
+
it("should throw an error when invalid option types are provided", async () => {
|
|
161
|
+
await expect(testing_1.Test.createTestingModule({
|
|
162
|
+
imports: [
|
|
163
|
+
DynModule.forRoot({
|
|
164
|
+
// @ts-expect-error: we are passing a wrong and unchecked field
|
|
165
|
+
useFlags: "not-a-boolean", // Wrong type
|
|
166
|
+
clientName: "some long enough client name",
|
|
167
|
+
}),
|
|
168
|
+
],
|
|
169
|
+
}).compile()).rejects.toThrow();
|
|
170
|
+
});
|
|
171
|
+
it("should throw an error when injected token is not found and not optional", async () => {
|
|
172
|
+
const NON_EXISTENT_TOKEN = "NON_EXISTENT_TOKEN";
|
|
173
|
+
await expect(testing_1.Test.createTestingModule({
|
|
174
|
+
imports: [
|
|
175
|
+
DynModule.forRootAsync({
|
|
176
|
+
useFactory: () => ({
|
|
177
|
+
useFlags: true,
|
|
178
|
+
clientName: "some long enough client name",
|
|
179
|
+
}),
|
|
180
|
+
inject: [{ token: NON_EXISTENT_TOKEN, optional: false }],
|
|
181
|
+
}),
|
|
182
|
+
],
|
|
183
|
+
}).compile()).rejects.toThrow();
|
|
184
|
+
});
|
|
185
|
+
});
|
|
186
|
+
//# sourceMappingURL=module.spec.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"module.spec.js","sourceRoot":"","sources":["../../src/module/module.spec.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@proventuslabs/nestjs-zod",
|
|
3
|
+
"version": "1.0.0-rc.1",
|
|
4
|
+
"license": "MIT",
|
|
5
|
+
"description": "A collection of NestJS modules to integrate Zod into your application.",
|
|
6
|
+
"keywords": [
|
|
7
|
+
"nestjs",
|
|
8
|
+
"zod",
|
|
9
|
+
"config",
|
|
10
|
+
"module"
|
|
11
|
+
],
|
|
12
|
+
"author": "ProventusLabs <info@proventuslabs.com>",
|
|
13
|
+
"homepage": "https://github.com/proventuslabs/nestjs/tree/main/packages/zod#readme",
|
|
14
|
+
"repository": {
|
|
15
|
+
"type": "git",
|
|
16
|
+
"url": "https://github.com/proventuslabs/nestjs.git",
|
|
17
|
+
"directory": "packages/zod"
|
|
18
|
+
},
|
|
19
|
+
"bugs": {
|
|
20
|
+
"url": "https://github.com/proventuslabs/nestjs/issues"
|
|
21
|
+
},
|
|
22
|
+
"main": "dist/index.js",
|
|
23
|
+
"types": "dist/index.d.ts",
|
|
24
|
+
"files": [
|
|
25
|
+
"dist"
|
|
26
|
+
],
|
|
27
|
+
"scripts": {
|
|
28
|
+
"prepublishOnly": "npm run build",
|
|
29
|
+
"build": "nest build",
|
|
30
|
+
"lint": "biome check .",
|
|
31
|
+
"lint:fix": "biome check . --write",
|
|
32
|
+
"format": "biome format .",
|
|
33
|
+
"format:fix": "biome format . --write",
|
|
34
|
+
"typecheck": "tsc --noEmit",
|
|
35
|
+
"test": "jest",
|
|
36
|
+
"test:watch": "jest --watch",
|
|
37
|
+
"test:coverage": "jest --coverage",
|
|
38
|
+
"test:coverage:watch": "jest --coverage --watch"
|
|
39
|
+
},
|
|
40
|
+
"dependencies": {
|
|
41
|
+
"lodash": "^4.17.21",
|
|
42
|
+
"yaml": "^2.8.0"
|
|
43
|
+
},
|
|
44
|
+
"peerDependencies": {
|
|
45
|
+
"@nestjs/common": "^10.0.0 || ^11.0.0",
|
|
46
|
+
"@nestjs/config": "^3.0.0 || ^4.0.0",
|
|
47
|
+
"zod": "^3.0.0"
|
|
48
|
+
},
|
|
49
|
+
"devDependencies": {
|
|
50
|
+
"@nestjs/cli": "^11.0.0",
|
|
51
|
+
"@nestjs/common": "^11.0.0",
|
|
52
|
+
"@nestjs/core": "^11.0.0",
|
|
53
|
+
"@nestjs/platform-express": "^11.0.0",
|
|
54
|
+
"@nestjs/testing": "^11.0.0",
|
|
55
|
+
"@types/jest": "^30.0.0",
|
|
56
|
+
"@types/lodash": "^4.17.18",
|
|
57
|
+
"@types/node": "^20.0.0",
|
|
58
|
+
"jest": "^30.0.0",
|
|
59
|
+
"reflect-metadata": "^0.2.2",
|
|
60
|
+
"rxjs": "^7.8.2",
|
|
61
|
+
"ts-jest": "^29.4.0",
|
|
62
|
+
"type-fest": "^4.41.0",
|
|
63
|
+
"typescript": "^5.8.3",
|
|
64
|
+
"zod": "^3.25.67"
|
|
65
|
+
}
|
|
66
|
+
}
|