@namigbit/config 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +82 -0
- package/dist/Env.d.ts +14 -0
- package/dist/Env.js +21 -0
- package/dist/getConfig.d.ts +2 -0
- package/dist/getConfig.js +34 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +18 -0
- package/dist/transformValue.d.ts +2 -0
- package/dist/transformValue.js +27 -0
- package/package.json +39 -0
package/README.md
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# @namig/config
|
|
2
|
+
|
|
3
|
+
Конфигурация через декораторы с валидацией.
|
|
4
|
+
|
|
5
|
+
## Установка
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @namig/config class-validator reflect-metadata dotenv
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Использование
|
|
12
|
+
|
|
13
|
+
```typescript
|
|
14
|
+
import 'reflect-metadata';
|
|
15
|
+
import { Env, getConfig } from '@namig/config';
|
|
16
|
+
import { IsString, IsNumber, Min, Max } from 'class-validator';
|
|
17
|
+
|
|
18
|
+
class AppConfig {
|
|
19
|
+
@Env('PORT', { type: 'number', default: 3000 })
|
|
20
|
+
@IsNumber()
|
|
21
|
+
@Min(1000)
|
|
22
|
+
@Max(65535)
|
|
23
|
+
port: number;
|
|
24
|
+
|
|
25
|
+
@Env('DATABASE_URL')
|
|
26
|
+
@IsString()
|
|
27
|
+
databaseUrl: string;
|
|
28
|
+
|
|
29
|
+
@Env('DEBUG', { type: 'boolean', default: false })
|
|
30
|
+
debug: boolean;
|
|
31
|
+
|
|
32
|
+
@Env('REDIS_CONFIG', { type: 'json', default: { host: 'localhost', port: 6379 } })
|
|
33
|
+
redis: { host: string; port: number };
|
|
34
|
+
|
|
35
|
+
@Env('ALLOWED_ORIGINS', { type: 'array', default: ['http://localhost:3000'] })
|
|
36
|
+
allowedOrigins: string[];
|
|
37
|
+
|
|
38
|
+
@Env('OPTIONAL_KEY', { optional: true })
|
|
39
|
+
optionalKey?: string;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export const config = getConfig(AppConfig);
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## API
|
|
46
|
+
|
|
47
|
+
### `@Env(key, options?)`
|
|
48
|
+
|
|
49
|
+
Декоратор для маппинга env переменной на свойство класса.
|
|
50
|
+
|
|
51
|
+
**Options:**
|
|
52
|
+
|
|
53
|
+
| Параметр | Тип | По умолчанию | Описание |
|
|
54
|
+
|----------|-----|--------------|----------|
|
|
55
|
+
| `type` | `'string' \| 'number' \| 'boolean' \| 'json' \| 'array'` | `'string'` | Тип преобразования |
|
|
56
|
+
| `default` | `any` | `undefined` | Значение по умолчанию |
|
|
57
|
+
| `optional` | `boolean` | `false` | Опциональная переменная |
|
|
58
|
+
|
|
59
|
+
### `getConfig<T>(ConfigClass)`
|
|
60
|
+
|
|
61
|
+
Создаёт и валидирует инстанс конфига.
|
|
62
|
+
|
|
63
|
+
- Читает `.env` файл через dotenv
|
|
64
|
+
- Трансформирует значения согласно `type`
|
|
65
|
+
- Валидирует через class-validator
|
|
66
|
+
- Выбрасывает ошибку если обязательная переменная отсутствует или валидация не прошла
|
|
67
|
+
|
|
68
|
+
## Типы преобразования
|
|
69
|
+
|
|
70
|
+
| Тип | ENV значение | Результат |
|
|
71
|
+
|-----|--------------|-----------|
|
|
72
|
+
| `string` | `"hello"` | `"hello"` |
|
|
73
|
+
| `number` | `"3000"` | `3000` |
|
|
74
|
+
| `boolean` | `"true"` / `"false"` | `true` / `false` |
|
|
75
|
+
| `json` | `'{"host":"localhost"}'` | `{ host: "localhost" }` |
|
|
76
|
+
| `array` | `"a, b, c"` | `["a", "b", "c"]` |
|
|
77
|
+
|
|
78
|
+
## Peer Dependencies
|
|
79
|
+
|
|
80
|
+
- `class-validator` ^0.14.0
|
|
81
|
+
- `dotenv` ^16.0.0
|
|
82
|
+
- `reflect-metadata` ^0.2.0
|
package/dist/Env.d.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import 'reflect-metadata';
|
|
2
|
+
export type EnvType = 'string' | 'number' | 'boolean' | 'json' | 'array';
|
|
3
|
+
export interface EnvOptions {
|
|
4
|
+
type?: EnvType;
|
|
5
|
+
default?: any;
|
|
6
|
+
optional?: boolean;
|
|
7
|
+
}
|
|
8
|
+
export interface EnvMetadata {
|
|
9
|
+
key: string;
|
|
10
|
+
propertyKey: string;
|
|
11
|
+
options: Required<EnvOptions>;
|
|
12
|
+
}
|
|
13
|
+
export declare const ENV_METADATA_KEY: unique symbol;
|
|
14
|
+
export declare function Env(key: string, options?: EnvOptions): (target: any, propertyKey: string) => void;
|
package/dist/Env.js
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ENV_METADATA_KEY = void 0;
|
|
4
|
+
exports.Env = Env;
|
|
5
|
+
require("reflect-metadata");
|
|
6
|
+
exports.ENV_METADATA_KEY = Symbol('env');
|
|
7
|
+
function Env(key, options = {}) {
|
|
8
|
+
return function (target, propertyKey) {
|
|
9
|
+
const metadata = Reflect.getMetadata(exports.ENV_METADATA_KEY, target) || [];
|
|
10
|
+
metadata.push({
|
|
11
|
+
key,
|
|
12
|
+
propertyKey,
|
|
13
|
+
options: {
|
|
14
|
+
type: options.type || 'string',
|
|
15
|
+
default: options.default,
|
|
16
|
+
optional: options.optional || false,
|
|
17
|
+
},
|
|
18
|
+
});
|
|
19
|
+
Reflect.defineMetadata(exports.ENV_METADATA_KEY, metadata, target);
|
|
20
|
+
};
|
|
21
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getConfig = getConfig;
|
|
4
|
+
require("reflect-metadata");
|
|
5
|
+
const class_validator_1 = require("class-validator");
|
|
6
|
+
const Env_1 = require("./Env");
|
|
7
|
+
const transformValue_1 = require("./transformValue");
|
|
8
|
+
const dotenv_1 = require("dotenv");
|
|
9
|
+
(0, dotenv_1.config)();
|
|
10
|
+
function getConfig(ConfigClass) {
|
|
11
|
+
const instance = new ConfigClass();
|
|
12
|
+
const metadata = Reflect.getMetadata(Env_1.ENV_METADATA_KEY, instance) || [];
|
|
13
|
+
for (const { key, propertyKey, options } of metadata) {
|
|
14
|
+
const value = process.env[key];
|
|
15
|
+
try {
|
|
16
|
+
const transformedValue = value ? (0, transformValue_1.transformValue)(value, options.type) : undefined;
|
|
17
|
+
if (!options.optional && !value && options.default === undefined) {
|
|
18
|
+
throw new Error(`Required env ${key} is missing`);
|
|
19
|
+
}
|
|
20
|
+
instance[propertyKey] = transformedValue ?? options.default;
|
|
21
|
+
}
|
|
22
|
+
catch (error) {
|
|
23
|
+
throw new Error(`Error processing ${key}: ${error.message}`);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
const errors = (0, class_validator_1.validateSync)(instance, {
|
|
27
|
+
skipMissingProperties: false,
|
|
28
|
+
forbidUnknownValues: false,
|
|
29
|
+
});
|
|
30
|
+
if (errors.length > 0) {
|
|
31
|
+
throw new Error(errors.map((error) => Object.values(error.constraints).join(', ')).join('\n'));
|
|
32
|
+
}
|
|
33
|
+
return instance;
|
|
34
|
+
}
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
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("./Env"), exports);
|
|
18
|
+
__exportStar(require("./getConfig"), exports);
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.transformValue = transformValue;
|
|
4
|
+
function transformValue(value, type) {
|
|
5
|
+
if (!value)
|
|
6
|
+
return undefined;
|
|
7
|
+
try {
|
|
8
|
+
switch (type) {
|
|
9
|
+
case 'number':
|
|
10
|
+
const num = Number(value);
|
|
11
|
+
if (isNaN(num))
|
|
12
|
+
throw new Error('Invalid number');
|
|
13
|
+
return num;
|
|
14
|
+
case 'boolean':
|
|
15
|
+
return value.toLowerCase() === 'true';
|
|
16
|
+
case 'json':
|
|
17
|
+
return JSON.parse(value);
|
|
18
|
+
case 'array':
|
|
19
|
+
return value.split(',').map((item) => item.trim());
|
|
20
|
+
default:
|
|
21
|
+
return value;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
catch (error) {
|
|
25
|
+
throw new Error(`Failed to transform value: ${error.message}`);
|
|
26
|
+
}
|
|
27
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@namigbit/config",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "Configuration module with validation",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"files": [
|
|
8
|
+
"dist"
|
|
9
|
+
],
|
|
10
|
+
"scripts": {
|
|
11
|
+
"build": "tsc",
|
|
12
|
+
"build:watch": "tsc --watch"
|
|
13
|
+
},
|
|
14
|
+
"peerDependencies": {
|
|
15
|
+
"class-validator": "^0.14.0",
|
|
16
|
+
"dotenv": "^16.0.0",
|
|
17
|
+
"reflect-metadata": "^0.2.0"
|
|
18
|
+
},
|
|
19
|
+
"devDependencies": {
|
|
20
|
+
"@types/node": "^22.0.0",
|
|
21
|
+
"class-validator": "^0.14.0",
|
|
22
|
+
"dotenv": "^16.0.0",
|
|
23
|
+
"reflect-metadata": "^0.2.0",
|
|
24
|
+
"typescript": "^5.0.0"
|
|
25
|
+
},
|
|
26
|
+
"repository": {
|
|
27
|
+
"type": "git",
|
|
28
|
+
"url": "https://github.com/namig/namig-libs.git",
|
|
29
|
+
"directory": "packages/config"
|
|
30
|
+
},
|
|
31
|
+
"keywords": [
|
|
32
|
+
"config",
|
|
33
|
+
"configuration",
|
|
34
|
+
"env",
|
|
35
|
+
"validation"
|
|
36
|
+
],
|
|
37
|
+
"author": "namig",
|
|
38
|
+
"license": "MIT"
|
|
39
|
+
}
|