@tsed/config 8.29.0 → 8.30.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +5 -5
- package/readme.md +11 -6
- package/lib/tsconfig.esm.tsbuildinfo +0 -1
- package/src/constants/constants.ts +0 -4
- package/src/decorators/injectConfigSource.integration.spec.ts +0 -43
- package/src/decorators/injectConfigSource.ts +0 -15
- package/src/fn/injectConfigSource.integration.spec.ts +0 -42
- package/src/fn/injectConfigSource.ts +0 -20
- package/src/hooks/afterResolveConfiguration.ts +0 -87
- package/src/index.ts +0 -9
- package/src/interfaces/ConfigSource.ts +0 -68
- package/src/interfaces/index.ts +0 -12
- package/src/providers/dotenv/DotEnvsConfigSource.ts +0 -40
- package/src/providers/dotenv/index.ts +0 -3
- package/src/providers/envs/EnvsConfigSource.spec.ts +0 -51
- package/src/providers/envs/EnvsConfigSource.ts +0 -23
- package/src/providers/envs/index.ts +0 -3
- package/src/providers/json/JsonConfigSource.spec.ts +0 -60
- package/src/providers/json/JsonConfigSource.ts +0 -45
- package/src/providers/json/index.ts +0 -3
- package/src/providers/yaml/YamlConfigSource.spec.ts +0 -64
- package/src/providers/yaml/YamlConfigSource.ts +0 -39
- package/src/providers/yaml/index.ts +0 -3
- package/src/utils/getConfigSources.ts +0 -52
- package/src/utils/jsonParse.ts +0 -7
- package/src/utils/validate.ts +0 -43
- package/src/utils/withOptions.ts +0 -19
- package/tsconfig.esm.json +0 -14
- package/vitest.config.mts +0 -21
|
@@ -1,87 +0,0 @@
|
|
|
1
|
-
import {configuration, constant, injectable, logger} from "@tsed/di";
|
|
2
|
-
import {$off, $on, $once} from "@tsed/hooks";
|
|
3
|
-
|
|
4
|
-
import {CONFIG_SOURCES} from "../constants/constants.js";
|
|
5
|
-
import type {ConfigurationExtends} from "../interfaces/ConfigSource.js";
|
|
6
|
-
import {getConfigSources} from "../utils/getConfigSources.js";
|
|
7
|
-
import {validate} from "../utils/validate.js";
|
|
8
|
-
|
|
9
|
-
export async function afterResolveConfiguration() {
|
|
10
|
-
const sources = getConfigSources(constant<ConfigurationExtends>("extends", []));
|
|
11
|
-
|
|
12
|
-
for (const source of sources.values()) {
|
|
13
|
-
const {instance, watch, refreshOn, name, validationSchema} = source;
|
|
14
|
-
const seed = Math.floor(Math.random() * 10000);
|
|
15
|
-
const ref = name + "-" + seed;
|
|
16
|
-
|
|
17
|
-
if (instance?.$onInit) {
|
|
18
|
-
await instance.$onInit();
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
const getAll = instance.getAll;
|
|
22
|
-
|
|
23
|
-
const refresh = async () => {
|
|
24
|
-
let data = await getAll.apply(instance);
|
|
25
|
-
|
|
26
|
-
if (validationSchema) {
|
|
27
|
-
const {validate} = await import("../utils/validate.js");
|
|
28
|
-
data = validate(name!, data, validationSchema);
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
configuration().set(data);
|
|
32
|
-
configuration().set(`configs.${name}`, data);
|
|
33
|
-
};
|
|
34
|
-
|
|
35
|
-
instance.getAll = refresh as any;
|
|
36
|
-
|
|
37
|
-
await refresh();
|
|
38
|
-
|
|
39
|
-
// manage watcher
|
|
40
|
-
if (watch && instance?.watch) {
|
|
41
|
-
const closer = await instance.watch(refresh);
|
|
42
|
-
|
|
43
|
-
closer && $once("$onDestroy", ref, closer);
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
// manager refresh on request
|
|
47
|
-
if (refreshOn === "request") {
|
|
48
|
-
$on("$onRequest", ref, refresh);
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
// manager refresh on response
|
|
52
|
-
if (refreshOn === "response") {
|
|
53
|
-
const onResponse = () => {
|
|
54
|
-
refresh().catch((error) => {
|
|
55
|
-
logger().error({
|
|
56
|
-
event: "CONFIG_SOURCE_REFRESH_ERROR",
|
|
57
|
-
hook: "$onResponse",
|
|
58
|
-
error_name: error.name,
|
|
59
|
-
error_message: error.message,
|
|
60
|
-
error_stack: error.stack
|
|
61
|
-
});
|
|
62
|
-
});
|
|
63
|
-
};
|
|
64
|
-
|
|
65
|
-
$on("$onResponse", ref, onResponse);
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
$once("$onDestroy", ref, () => {
|
|
69
|
-
if (instance?.$onDestroy) {
|
|
70
|
-
// clean all event related to the same ref
|
|
71
|
-
$off(ref);
|
|
72
|
-
|
|
73
|
-
return instance.$onDestroy();
|
|
74
|
-
}
|
|
75
|
-
});
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
const configs = Object.fromEntries(
|
|
79
|
-
[...sources.entries()].map(([key, source]) => {
|
|
80
|
-
return [key, source.instance];
|
|
81
|
-
})
|
|
82
|
-
);
|
|
83
|
-
|
|
84
|
-
injectable(CONFIG_SOURCES).value(configs);
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
$on("$afterResolveConfiguration", afterResolveConfiguration);
|
package/src/index.ts
DELETED
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @file Automatically generated by @tsed/barrels.
|
|
3
|
-
*/
|
|
4
|
-
export * from "./constants/constants.js";
|
|
5
|
-
export * from "./hooks/afterResolveConfiguration.js";
|
|
6
|
-
export * from "./interfaces/ConfigSource.js";
|
|
7
|
-
export * from "./interfaces/index.js";
|
|
8
|
-
export * from "./utils/getConfigSources.js";
|
|
9
|
-
export * from "./utils/withOptions.js";
|
|
@@ -1,68 +0,0 @@
|
|
|
1
|
-
import type {Type} from "@tsed/core";
|
|
2
|
-
|
|
3
|
-
export interface ConfigSourceOptions<Opts = any> {
|
|
4
|
-
/**
|
|
5
|
-
* The name of the configuration provider.
|
|
6
|
-
*/
|
|
7
|
-
name?: string;
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* The priority of the configuration provider. Higher priority providers will override values from lower priority providers.
|
|
11
|
-
* @default 0
|
|
12
|
-
*/
|
|
13
|
-
priority?: number;
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* Whether to enable the configuration provider.
|
|
17
|
-
* @default true
|
|
18
|
-
*/
|
|
19
|
-
enabled?: boolean;
|
|
20
|
-
/**
|
|
21
|
-
* The ConfigSource to use for loading configuration values.
|
|
22
|
-
* @param args
|
|
23
|
-
*/
|
|
24
|
-
use: Type<ConfigSource<Opts>>;
|
|
25
|
-
/**
|
|
26
|
-
* Options for the configuration provider.
|
|
27
|
-
*/
|
|
28
|
-
options: Opts;
|
|
29
|
-
/**
|
|
30
|
-
* A JsonSchema instance to validate the configuration values against.
|
|
31
|
-
*/
|
|
32
|
-
validationSchema?: {toJSON(opts: Record<string, unknown>): Record<string, unknown> & {required?: string[]}};
|
|
33
|
-
/**
|
|
34
|
-
* Enable FileSystem Watch mode. ConfigSource must implement the `watch()` method.
|
|
35
|
-
*/
|
|
36
|
-
watch?: boolean;
|
|
37
|
-
/**
|
|
38
|
-
* Refresh strategy. Ts.ED can refresh ConfigSource during the "$onRequest" or "$onResponse" hook.
|
|
39
|
-
*
|
|
40
|
-
* Note: refresh data during `$onRequest` can degrade performance depending on the ConfigSource type.
|
|
41
|
-
* We recommend to refresh data during the $onResponse hook (action are asynchronous).
|
|
42
|
-
*/
|
|
43
|
-
refreshOn?: "request" | "response";
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
export type InitialConfigSourceOptions = Omit<ConfigSourceOptions, "options" | "use">;
|
|
47
|
-
export type ConfigSourceWatchCloseCB = () => Promise<void> | void;
|
|
48
|
-
export type ConfigSourceOnChangeCB = () => Promise<void>;
|
|
49
|
-
|
|
50
|
-
/**
|
|
51
|
-
* Interface for a configuration source that can provide configuration values.
|
|
52
|
-
*/
|
|
53
|
-
export interface ConfigSource<Opts = any> {
|
|
54
|
-
options: Opts;
|
|
55
|
-
|
|
56
|
-
$onInit?(): Promise<void> | void;
|
|
57
|
-
|
|
58
|
-
$onDestroy?(): Promise<void> | void;
|
|
59
|
-
|
|
60
|
-
/**
|
|
61
|
-
* Load configuration from the source.
|
|
62
|
-
*/
|
|
63
|
-
getAll(): Promise<Record<string, unknown>> | Record<string, unknown>;
|
|
64
|
-
|
|
65
|
-
watch?(onChange?: ConfigSourceOnChangeCB): Promise<ConfigSourceWatchCloseCB> | ConfigSourceWatchCloseCB | void | Promise<void>;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
export type ConfigurationExtends = (Type<ConfigSource> | ConfigSourceOptions)[];
|
package/src/interfaces/index.ts
DELETED
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import type {ConfigurationExtends} from "./ConfigSource.js";
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Extend the global TsED namespace to include configuration for this package.
|
|
5
|
-
*/
|
|
6
|
-
declare global {
|
|
7
|
-
namespace TsED {
|
|
8
|
-
interface Configuration {
|
|
9
|
-
extends?: ConfigurationExtends;
|
|
10
|
-
}
|
|
11
|
-
}
|
|
12
|
-
}
|
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
import {watch} from "node:fs";
|
|
2
|
-
import {join} from "node:path";
|
|
3
|
-
|
|
4
|
-
import dotenvExpand from "dotenv-expand";
|
|
5
|
-
import dotenv, {type DotenvFlowConfigOptions} from "dotenv-flow";
|
|
6
|
-
import {globby} from "globby";
|
|
7
|
-
|
|
8
|
-
import type {ConfigSourceOnChangeCB} from "../../interfaces/ConfigSource.js";
|
|
9
|
-
import {EnvsConfigSource, type EnvsConfigSourceOptions} from "../envs/EnvsConfigSource.js";
|
|
10
|
-
|
|
11
|
-
export type DotEnvsConfigSourceOptions = EnvsConfigSourceOptions & DotenvFlowConfigOptions;
|
|
12
|
-
|
|
13
|
-
export class DotEnvsConfigSource extends EnvsConfigSource<DotEnvsConfigSourceOptions> {
|
|
14
|
-
getAll(): Record<string, unknown> {
|
|
15
|
-
dotenvExpand.expand(dotenv.config(this.options));
|
|
16
|
-
|
|
17
|
-
return super.getAll();
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
async watch(onChange?: ConfigSourceOnChangeCB) {
|
|
21
|
-
const files = await globby(join(this.options.path!, ".env"), {
|
|
22
|
-
dot: true,
|
|
23
|
-
absolute: true
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
const watchers = files.map((file) => {
|
|
27
|
-
const watcher = watch(file, onChange);
|
|
28
|
-
|
|
29
|
-
return () => {
|
|
30
|
-
watcher.close();
|
|
31
|
-
};
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
return () => {
|
|
35
|
-
watchers.map((close) => {
|
|
36
|
-
return close();
|
|
37
|
-
});
|
|
38
|
-
};
|
|
39
|
-
}
|
|
40
|
-
}
|
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
import {EnvsConfigSource} from "./EnvsConfigSource.js";
|
|
2
|
-
|
|
3
|
-
describe("EnvsConfigSource", () => {
|
|
4
|
-
describe("getAll()", () => {
|
|
5
|
-
it("should return all environment variables", () => {
|
|
6
|
-
process.env.TEST_VAR = "test";
|
|
7
|
-
process.env.TEST_VAR_2 = "test2";
|
|
8
|
-
|
|
9
|
-
const source = new EnvsConfigSource();
|
|
10
|
-
source.options = {};
|
|
11
|
-
|
|
12
|
-
const result = source.getAll();
|
|
13
|
-
|
|
14
|
-
expect(result.TEST_VAR).toEqual("test");
|
|
15
|
-
expect(result.TEST_VAR_2).toEqual("test2");
|
|
16
|
-
});
|
|
17
|
-
|
|
18
|
-
it("should parse JSON values if parseJson is true", () => {
|
|
19
|
-
process.env.TEST_JSON = '{"key":"value"}';
|
|
20
|
-
|
|
21
|
-
const source = new EnvsConfigSource();
|
|
22
|
-
source.options = {parseJson: true};
|
|
23
|
-
|
|
24
|
-
const result = source.getAll();
|
|
25
|
-
|
|
26
|
-
expect(result.TEST_JSON).toEqual({key: "value"});
|
|
27
|
-
});
|
|
28
|
-
|
|
29
|
-
it("should not parse JSON values if parseJson is false", () => {
|
|
30
|
-
process.env.TEST_JSON = '{"key":"value"}';
|
|
31
|
-
|
|
32
|
-
const source = new EnvsConfigSource();
|
|
33
|
-
source.options = {parseJson: false};
|
|
34
|
-
|
|
35
|
-
const result = source.getAll();
|
|
36
|
-
|
|
37
|
-
expect(result.TEST_JSON).toEqual('{"key":"value"}');
|
|
38
|
-
});
|
|
39
|
-
it("should handle invalid JSON gracefully", () => {
|
|
40
|
-
process.env.INVALID_JSON = "{invalid json}";
|
|
41
|
-
|
|
42
|
-
const source = new EnvsConfigSource();
|
|
43
|
-
source.options = {parseJson: true};
|
|
44
|
-
|
|
45
|
-
const result = source.getAll();
|
|
46
|
-
|
|
47
|
-
// Check that it falls back to the string value when parsing fails
|
|
48
|
-
expect(result.INVALID_JSON).toEqual("{invalid json}");
|
|
49
|
-
});
|
|
50
|
-
});
|
|
51
|
-
});
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import type {ConfigSource} from "../../interfaces/ConfigSource.js";
|
|
2
|
-
import {jsonParse} from "../../utils/jsonParse.js";
|
|
3
|
-
|
|
4
|
-
export interface EnvsConfigSourceOptions {
|
|
5
|
-
parseJson?: boolean;
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
export class EnvsConfigSource<Config extends EnvsConfigSourceOptions = EnvsConfigSourceOptions> implements ConfigSource<Config> {
|
|
9
|
-
options!: Config;
|
|
10
|
-
|
|
11
|
-
getAll(): Record<string, unknown> {
|
|
12
|
-
const {parseJson = true} = this.options;
|
|
13
|
-
|
|
14
|
-
return Object.entries(process.env).reduce((obj, [key, value]) => {
|
|
15
|
-
const formattedValue = parseJson ? jsonParse(value!) : value;
|
|
16
|
-
|
|
17
|
-
return {
|
|
18
|
-
...obj,
|
|
19
|
-
[key]: formattedValue
|
|
20
|
-
};
|
|
21
|
-
}, {});
|
|
22
|
-
}
|
|
23
|
-
}
|
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
import {existsSync, readFileSync, watch} from "node:fs";
|
|
2
|
-
|
|
3
|
-
import {JsonConfigSource} from "./JsonConfigSource.js";
|
|
4
|
-
|
|
5
|
-
vi.mock("node:fs");
|
|
6
|
-
describe("JsonConfigSource", () => {
|
|
7
|
-
describe("getAll()", () => {
|
|
8
|
-
it("should read JSON file and return parsed object", () => {
|
|
9
|
-
const source = new JsonConfigSource();
|
|
10
|
-
source.options = {
|
|
11
|
-
path: "./test.json"
|
|
12
|
-
};
|
|
13
|
-
|
|
14
|
-
vi.mocked(existsSync).mockReturnValue(true);
|
|
15
|
-
vi.mocked(readFileSync).mockReturnValue(JSON.stringify({key: "value"}));
|
|
16
|
-
|
|
17
|
-
const result = source.getAll();
|
|
18
|
-
|
|
19
|
-
expect(result).toEqual({key: "value"});
|
|
20
|
-
});
|
|
21
|
-
it("should return empty object if file does not exist", () => {
|
|
22
|
-
const source = new JsonConfigSource();
|
|
23
|
-
source.options = {
|
|
24
|
-
path: "./test.json"
|
|
25
|
-
};
|
|
26
|
-
|
|
27
|
-
vi.mocked(existsSync).mockReturnValue(false);
|
|
28
|
-
|
|
29
|
-
const result = source.getAll();
|
|
30
|
-
|
|
31
|
-
expect(result).toEqual({});
|
|
32
|
-
});
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
describe("watch()", () => {
|
|
36
|
-
it("should call watch with the correct path", () => {
|
|
37
|
-
const source = new JsonConfigSource();
|
|
38
|
-
source.options = {
|
|
39
|
-
path: "./test.json"
|
|
40
|
-
};
|
|
41
|
-
|
|
42
|
-
const mockOnChange = vi.fn();
|
|
43
|
-
|
|
44
|
-
const mockWatcher = {
|
|
45
|
-
close: vi.fn()
|
|
46
|
-
};
|
|
47
|
-
|
|
48
|
-
vi.mocked(watch).mockReturnValue(mockWatcher as any);
|
|
49
|
-
|
|
50
|
-
const result = source.watch(mockOnChange);
|
|
51
|
-
|
|
52
|
-
expect(watch).toHaveBeenCalledWith("./test.json", mockOnChange);
|
|
53
|
-
expect(result).toBeInstanceOf(Function);
|
|
54
|
-
|
|
55
|
-
result();
|
|
56
|
-
|
|
57
|
-
expect(mockWatcher.close).toHaveBeenCalled();
|
|
58
|
-
});
|
|
59
|
-
});
|
|
60
|
-
});
|
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
import {existsSync, readFileSync, watch} from "node:fs";
|
|
2
|
-
|
|
3
|
-
import {logger} from "@tsed/di";
|
|
4
|
-
|
|
5
|
-
import type {ConfigSource, ConfigSourceOnChangeCB} from "../../interfaces/ConfigSource.js";
|
|
6
|
-
|
|
7
|
-
export interface JsonConfigSourceOptions {
|
|
8
|
-
/**
|
|
9
|
-
* The path to the JSON file.
|
|
10
|
-
*/
|
|
11
|
-
path: string;
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* The encoding to use when reading the file.
|
|
15
|
-
* @default "utf8"
|
|
16
|
-
*/
|
|
17
|
-
encoding?: BufferEncoding;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
export class JsonConfigSource implements ConfigSource<JsonConfigSourceOptions> {
|
|
21
|
-
options!: JsonConfigSourceOptions;
|
|
22
|
-
|
|
23
|
-
getAll(): Record<string, unknown> {
|
|
24
|
-
const {path, encoding = "utf8"} = this.options;
|
|
25
|
-
// Check if the file exists
|
|
26
|
-
if (!existsSync(path)) {
|
|
27
|
-
logger().warn(`Configuration file not found: ${path}`);
|
|
28
|
-
return {};
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
// Read the file
|
|
32
|
-
const fileContent = readFileSync(path, encoding);
|
|
33
|
-
|
|
34
|
-
return JSON.parse(fileContent);
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
watch(onChange: ConfigSourceOnChangeCB) {
|
|
38
|
-
const {path} = this.options;
|
|
39
|
-
const watcher = watch(path, onChange);
|
|
40
|
-
|
|
41
|
-
return () => {
|
|
42
|
-
watcher.close();
|
|
43
|
-
};
|
|
44
|
-
}
|
|
45
|
-
}
|
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
import {existsSync, readFileSync, watch} from "node:fs";
|
|
2
|
-
|
|
3
|
-
import JsYaml from "js-yaml";
|
|
4
|
-
|
|
5
|
-
import {YamlConfigSource} from "./YamlConfigSource.js";
|
|
6
|
-
|
|
7
|
-
vi.mock("node:fs");
|
|
8
|
-
vi.mock("js-yaml");
|
|
9
|
-
|
|
10
|
-
describe("YamlConfigSource", () => {
|
|
11
|
-
describe("getAll()", () => {
|
|
12
|
-
it("should read JSON file and return parsed object", async () => {
|
|
13
|
-
const source = new YamlConfigSource();
|
|
14
|
-
source.options = {
|
|
15
|
-
path: "./test.yaml"
|
|
16
|
-
};
|
|
17
|
-
|
|
18
|
-
vi.mocked(existsSync).mockReturnValue(true);
|
|
19
|
-
vi.mocked(JsYaml.load).mockReturnValue({key: "value"});
|
|
20
|
-
|
|
21
|
-
const result = await source.getAll();
|
|
22
|
-
|
|
23
|
-
expect(result).toEqual({key: "value"});
|
|
24
|
-
});
|
|
25
|
-
it("should return empty object if file does not exist", async () => {
|
|
26
|
-
const source = new YamlConfigSource();
|
|
27
|
-
source.options = {
|
|
28
|
-
path: "./test.yaml"
|
|
29
|
-
};
|
|
30
|
-
|
|
31
|
-
vi.mocked(existsSync).mockReturnValue(false);
|
|
32
|
-
|
|
33
|
-
const result = await source.getAll();
|
|
34
|
-
|
|
35
|
-
expect(result).toEqual({});
|
|
36
|
-
});
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
describe("watch()", () => {
|
|
40
|
-
it("should call watch with the correct path", () => {
|
|
41
|
-
const source = new YamlConfigSource();
|
|
42
|
-
source.options = {
|
|
43
|
-
path: "./test.yaml"
|
|
44
|
-
};
|
|
45
|
-
|
|
46
|
-
const mockOnChange = vi.fn();
|
|
47
|
-
|
|
48
|
-
const mockWatcher = {
|
|
49
|
-
close: vi.fn()
|
|
50
|
-
};
|
|
51
|
-
|
|
52
|
-
vi.mocked(watch).mockReturnValue(mockWatcher as any);
|
|
53
|
-
|
|
54
|
-
const result = source.watch(mockOnChange);
|
|
55
|
-
|
|
56
|
-
expect(watch).toHaveBeenCalledWith("./test.yaml", mockOnChange);
|
|
57
|
-
expect(result).toBeInstanceOf(Function);
|
|
58
|
-
|
|
59
|
-
result();
|
|
60
|
-
|
|
61
|
-
expect(mockWatcher.close).toHaveBeenCalled();
|
|
62
|
-
});
|
|
63
|
-
});
|
|
64
|
-
});
|
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
import {existsSync, watch} from "node:fs";
|
|
2
|
-
|
|
3
|
-
import {logger} from "@tsed/di";
|
|
4
|
-
import JsYaml, {type LoadOptions} from "js-yaml";
|
|
5
|
-
|
|
6
|
-
import type {ConfigSource, ConfigSourceOnChangeCB} from "../../interfaces/ConfigSource.js";
|
|
7
|
-
|
|
8
|
-
export interface YamlConfigSourceOptions extends LoadOptions {
|
|
9
|
-
/**
|
|
10
|
-
* The path to the JSON file.
|
|
11
|
-
*/
|
|
12
|
-
path: string;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export class YamlConfigSource implements ConfigSource<YamlConfigSourceOptions> {
|
|
16
|
-
options!: YamlConfigSourceOptions;
|
|
17
|
-
|
|
18
|
-
async getAll() {
|
|
19
|
-
const {path, ...opts} = this.options;
|
|
20
|
-
|
|
21
|
-
// Check if the file exists
|
|
22
|
-
if (!existsSync(path)) {
|
|
23
|
-
logger().warn(`Configuration file not found: ${path}`);
|
|
24
|
-
return {};
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
// Read the file
|
|
28
|
-
return (await JsYaml.load(path, opts)) as Record<string, unknown>;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
watch(onChange: ConfigSourceOnChangeCB) {
|
|
32
|
-
const {path} = this.options;
|
|
33
|
-
const watcher = watch(path, onChange);
|
|
34
|
-
|
|
35
|
-
return () => {
|
|
36
|
-
watcher.close();
|
|
37
|
-
};
|
|
38
|
-
}
|
|
39
|
-
}
|
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
import {nameOf, type Type} from "@tsed/core";
|
|
2
|
-
import {inject, injectable, ProviderScope} from "@tsed/di";
|
|
3
|
-
import {camelCase} from "change-case";
|
|
4
|
-
|
|
5
|
-
import type {ConfigSource, ConfigSourceOptions} from "../interfaces/ConfigSource.js";
|
|
6
|
-
|
|
7
|
-
export function getConfigSources(configSources: (Type<ConfigSource<any>> | ConfigSourceOptions)[]) {
|
|
8
|
-
return configSources
|
|
9
|
-
.map((configSource, index) => {
|
|
10
|
-
if (!("use" in configSource)) {
|
|
11
|
-
return {
|
|
12
|
-
name: camelCase(nameOf(configSource).replace("ConfigSource", "")),
|
|
13
|
-
use: configSource,
|
|
14
|
-
options: {},
|
|
15
|
-
priority: index,
|
|
16
|
-
enabled: true
|
|
17
|
-
} satisfies ConfigSourceOptions;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
return {
|
|
21
|
-
enabled: true,
|
|
22
|
-
...configSource,
|
|
23
|
-
priority: configSource.priority || 0,
|
|
24
|
-
name: configSource.name || camelCase(nameOf(configSource.use).replace("ConfigSource", ""))
|
|
25
|
-
} satisfies ConfigSourceOptions;
|
|
26
|
-
})
|
|
27
|
-
.sort((a, b) => a.priority! - b.priority!)
|
|
28
|
-
.filter(({enabled}) => enabled)
|
|
29
|
-
.reduce((map, options, index) => {
|
|
30
|
-
let {use, name, options: useOpts} = options;
|
|
31
|
-
|
|
32
|
-
injectable(use).scope(ProviderScope.INSTANCE);
|
|
33
|
-
|
|
34
|
-
const instance = inject(use, {
|
|
35
|
-
useOpts
|
|
36
|
-
});
|
|
37
|
-
|
|
38
|
-
instance.options = useOpts;
|
|
39
|
-
|
|
40
|
-
if (map.has(name)) {
|
|
41
|
-
name = `${name}_${index}`;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
map.set(name, {
|
|
45
|
-
...options,
|
|
46
|
-
instance,
|
|
47
|
-
data: {}
|
|
48
|
-
});
|
|
49
|
-
|
|
50
|
-
return map;
|
|
51
|
-
}, new Map<string, ConfigSourceOptions & {instance: ConfigSource<any>; data: Record<string, any>}>());
|
|
52
|
-
}
|
package/src/utils/jsonParse.ts
DELETED
package/src/utils/validate.ts
DELETED
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
import {Ajv, type ErrorObject} from "ajv";
|
|
2
|
-
import AjvErrors from "ajv-errors";
|
|
3
|
-
import AjvFormats from "ajv-formats";
|
|
4
|
-
|
|
5
|
-
const ajv = new Ajv({
|
|
6
|
-
verbose: false,
|
|
7
|
-
coerceTypes: true,
|
|
8
|
-
strict: false,
|
|
9
|
-
discriminator: true,
|
|
10
|
-
allErrors: true
|
|
11
|
-
});
|
|
12
|
-
|
|
13
|
-
// @ts-ignore
|
|
14
|
-
AjvErrors(ajv);
|
|
15
|
-
|
|
16
|
-
// @ts-ignore
|
|
17
|
-
AjvFormats(ajv as any);
|
|
18
|
-
|
|
19
|
-
class ValidationError extends Error {
|
|
20
|
-
private errors: ErrorObject[];
|
|
21
|
-
|
|
22
|
-
constructor(message: string, errors: ErrorObject[]) {
|
|
23
|
-
super(message);
|
|
24
|
-
this.name = "VALIDATION_ERROR";
|
|
25
|
-
this.errors = errors;
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
export function validate(configName: string, config: Record<string, unknown>, validationSchema: any) {
|
|
30
|
-
const schema = "toJSON" in validationSchema ? validationSchema.toJSON() : validationSchema;
|
|
31
|
-
|
|
32
|
-
const validate = ajv.compile(schema);
|
|
33
|
-
const result = validate(config);
|
|
34
|
-
|
|
35
|
-
if (!result && validate.errors) {
|
|
36
|
-
const firstError = validate.errors[0];
|
|
37
|
-
const message = `extends[${configName}].${firstError?.instancePath?.substring(1) || ""} ${firstError?.message}.`;
|
|
38
|
-
|
|
39
|
-
throw new ValidationError(message, validate.errors);
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
return config;
|
|
43
|
-
}
|
package/src/utils/withOptions.ts
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
import type {Type} from "@tsed/core";
|
|
2
|
-
|
|
3
|
-
import type {ConfigSource, InitialConfigSourceOptions} from "../interfaces/ConfigSource.js";
|
|
4
|
-
|
|
5
|
-
export function withOptions<Opts>(
|
|
6
|
-
use: Type<ConfigSource<Opts>>,
|
|
7
|
-
{name, priority, validationSchema, enabled, refreshOn, watch, ...opts}: InitialConfigSourceOptions & Opts
|
|
8
|
-
) {
|
|
9
|
-
return {
|
|
10
|
-
name,
|
|
11
|
-
priority,
|
|
12
|
-
validationSchema,
|
|
13
|
-
use,
|
|
14
|
-
watch,
|
|
15
|
-
refreshOn,
|
|
16
|
-
enabled: enabled !== false,
|
|
17
|
-
options: opts
|
|
18
|
-
};
|
|
19
|
-
}
|
package/tsconfig.esm.json
DELETED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"extends": "@tsed/typescript/tsconfig.node.json",
|
|
3
|
-
"compilerOptions": {
|
|
4
|
-
"baseUrl": ".",
|
|
5
|
-
"rootDir": "src",
|
|
6
|
-
"outDir": "./lib/esm",
|
|
7
|
-
"declarationDir": "./lib/types",
|
|
8
|
-
"declaration": true,
|
|
9
|
-
"composite": true,
|
|
10
|
-
"noEmit": false
|
|
11
|
-
},
|
|
12
|
-
"include": ["src/**/*.ts", "src/**/*.json"],
|
|
13
|
-
"exclude": ["node_modules", "test", "lib", "benchmark", "coverage", "spec", "**/*.benchmark.ts", "**/*.spec.ts", "keys", "**/__mock__/**"]
|
|
14
|
-
}
|