@keeex/projectconfig 6.0.1 → 7.0.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/README.md +99 -0
- package/lib/index.d.ts +3 -1
- package/lib/index.js +2 -0
- package/lib/services/cli.d.ts +26 -26
- package/lib/services/file.d.ts +26 -26
- package/lib/services/loader.d.ts +27 -27
- package/lib/services/loader.js +33 -8
- package/lib/services/singleton.d.ts +34 -0
- package/lib/services/singleton.js +63 -0
- package/lib/services/types.d.ts +76 -9
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -72,6 +72,97 @@ For secrets, the argument would be `--secrets.db.user` for `config.secrets.db.us
|
|
|
72
72
|
|
|
73
73
|
### Accessing the configuration
|
|
74
74
|
|
|
75
|
+
#### Automatic singleton
|
|
76
|
+
|
|
77
|
+
The recommended way to load configuration is by generating a singleton.
|
|
78
|
+
This allows asynchronous loading of the configuration on demand, but convenient synchronous access to the loaded object.
|
|
79
|
+
|
|
80
|
+
```typescript
|
|
81
|
+
import {makeProfilePredicate} from "@keeex/utils/types/record.js";
|
|
82
|
+
import * as projectConfig from "@keeex/projectconfig";
|
|
83
|
+
|
|
84
|
+
interface Paths {
|
|
85
|
+
importantDir: string;
|
|
86
|
+
webRoot: string;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const isPaths = makeProfilePredicate<Paths>({
|
|
90
|
+
importantDir: "string",
|
|
91
|
+
webRoot: "string",
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
interface Config {
|
|
95
|
+
debug: boolean;
|
|
96
|
+
paths: Paths;
|
|
97
|
+
port: number;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const isConfig = makeProfilePredicate<Config>({
|
|
101
|
+
debug: "boolean",
|
|
102
|
+
paths: isPaths,
|
|
103
|
+
port: "number",
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
interface Secrets {
|
|
107
|
+
login: string;
|
|
108
|
+
password: string;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const isSecrets = makeProfilePredicate<Secrets>({
|
|
112
|
+
login: "string";
|
|
113
|
+
password: "string";
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
const singleton = getConfigSingleton({
|
|
117
|
+
configDefault: {
|
|
118
|
+
port: 3000,
|
|
119
|
+
debug: false,
|
|
120
|
+
paths: {webRoot: "./dist"},
|
|
121
|
+
},
|
|
122
|
+
configEnvironmentVars: {
|
|
123
|
+
"port": projectConfig.TypeForEnv.number,
|
|
124
|
+
"debug": projectConfig.TypeForEnv.boolean,
|
|
125
|
+
"paths.importantDir": projectConfig.TypeForEnv.string,
|
|
126
|
+
"paths.webRoot": projectConfig.TypesForEnv.string,
|
|
127
|
+
},
|
|
128
|
+
configPredicate: isConfig,
|
|
129
|
+
secretsPredicate: isSecrets,
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
export const loadConfig = singleton.loadConfig;
|
|
133
|
+
export const config = singleton.config;
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
In the above exemple, we define a module that export both the loading function and the fully-typed configuration, to be used throughout a project.
|
|
137
|
+
|
|
138
|
+
You have to manually load the configuration, usually during your application initialization:
|
|
139
|
+
|
|
140
|
+
```typescript
|
|
141
|
+
import {loadConfig} from "./services/config.js";
|
|
142
|
+
|
|
143
|
+
await loadConfig();
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
Then, using the configuration would look something like this:
|
|
147
|
+
|
|
148
|
+
```typescript
|
|
149
|
+
import {config} from "./services/config.js";
|
|
150
|
+
|
|
151
|
+
console.log(`
|
|
152
|
+
Port: ${config.port}
|
|
153
|
+
Debug: ${config.debug}
|
|
154
|
+
ImportantDir: ${config.paths.importantDir}
|
|
155
|
+
WebRoot: ${config.paths.webRoot}
|
|
156
|
+
User: ${config.secrets.login}
|
|
157
|
+
Password: ${"*".repeat(config.secrets.password.length)}
|
|
158
|
+
`);
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
The loaded configuration is cached after the first loading call, so it is safe to recall this function multiple times from multiple places if needed.
|
|
162
|
+
Accessing the `config` object before calling `loadConfig()` will raise an exception.
|
|
163
|
+
|
|
164
|
+
#### Manual loading
|
|
165
|
+
|
|
75
166
|
The loaded configuration can be obtained by calling the function `loadConfig()`.
|
|
76
167
|
|
|
77
168
|
```typescript
|
|
@@ -149,3 +240,11 @@ Password: ${"*".repeat(config.secrets.password.length)}
|
|
|
149
240
|
```
|
|
150
241
|
|
|
151
242
|
The loaded configuration is cached after the first call, so it is safe to recall this function multiple times from multiple places if needed.
|
|
243
|
+
|
|
244
|
+
## Migration guide
|
|
245
|
+
|
|
246
|
+
### From 6.x to 7.x
|
|
247
|
+
|
|
248
|
+
- The config definition property `secretEnvironmentVars` is renamed to `secretsEnvironmentVars`
|
|
249
|
+
- The default genericity value for `SecretsType` changed from `never` to `undefined`
|
|
250
|
+
- Configuration now have two sets of generic types, `ConfigType` and `SecretsType` are loaded as before, but additionally `TypedConfigType` and `TypedSecretsType` can be provided in conjunction with the `convert` property on the configuration definition object to move from the fully loaded configuration to a custom typed loaded configuration.
|
package/lib/index.d.ts
CHANGED
|
@@ -26,4 +26,6 @@
|
|
|
26
26
|
*
|
|
27
27
|
*/
|
|
28
28
|
export { loadConfig } from "./services/loader.js";
|
|
29
|
-
export
|
|
29
|
+
export { TypeForEnv } from "./services/types.js";
|
|
30
|
+
export type { ProjectConfig, ConvertFunc } from "./services/types.js";
|
|
31
|
+
export { getConfigSingleton } from "./services/singleton.js";
|
package/lib/index.js
CHANGED
package/lib/services/cli.d.ts
CHANGED
|
@@ -1,30 +1,30 @@
|
|
|
1
1
|
/**
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
2
|
+
* @license
|
|
3
|
+
* @preserve
|
|
4
|
+
*
|
|
5
|
+
* KeeeX SAS Public code
|
|
6
|
+
* https://keeex.me
|
|
7
|
+
* Copyright 2013-2026 KeeeX All Rights Reserved.
|
|
8
|
+
*
|
|
9
|
+
* These computer program listings and specifications, herein,
|
|
10
|
+
* are and remain the property of KeeeX SAS. The intellectual
|
|
11
|
+
* and technical concepts herein are proprietary to KeeeX SAS
|
|
12
|
+
* and may be covered by EU and foreign patents,
|
|
13
|
+
* patents in process, trade secrets and copyright law.
|
|
14
|
+
*
|
|
15
|
+
* These listings are published as a way to provide third party
|
|
16
|
+
* with the ability to process KeeeX data.
|
|
17
|
+
* As such, support for public inquiries is limited.
|
|
18
|
+
* They are provided "as-is", without warrany of any kind.
|
|
19
|
+
*
|
|
20
|
+
* They shall not be reproduced or copied or used in whole or
|
|
21
|
+
* in part as the basis for manufacture or sale of items unless
|
|
22
|
+
* prior written permission is obtained from KeeeX SAS.
|
|
23
|
+
*
|
|
24
|
+
* For a license agreement, please contact:
|
|
25
|
+
* <mailto: contact@keeex.net>
|
|
26
|
+
*
|
|
27
|
+
*/
|
|
28
28
|
import type * as types from "./types.js";
|
|
29
29
|
import type { JSONObject } from "@keeex/utils/json.js";
|
|
30
30
|
/**
|
package/lib/services/file.d.ts
CHANGED
|
@@ -1,30 +1,30 @@
|
|
|
1
1
|
/**
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
2
|
+
* @license
|
|
3
|
+
* @preserve
|
|
4
|
+
*
|
|
5
|
+
* KeeeX SAS Public code
|
|
6
|
+
* https://keeex.me
|
|
7
|
+
* Copyright 2013-2026 KeeeX All Rights Reserved.
|
|
8
|
+
*
|
|
9
|
+
* These computer program listings and specifications, herein,
|
|
10
|
+
* are and remain the property of KeeeX SAS. The intellectual
|
|
11
|
+
* and technical concepts herein are proprietary to KeeeX SAS
|
|
12
|
+
* and may be covered by EU and foreign patents,
|
|
13
|
+
* patents in process, trade secrets and copyright law.
|
|
14
|
+
*
|
|
15
|
+
* These listings are published as a way to provide third party
|
|
16
|
+
* with the ability to process KeeeX data.
|
|
17
|
+
* As such, support for public inquiries is limited.
|
|
18
|
+
* They are provided "as-is", without warrany of any kind.
|
|
19
|
+
*
|
|
20
|
+
* They shall not be reproduced or copied or used in whole or
|
|
21
|
+
* in part as the basis for manufacture or sale of items unless
|
|
22
|
+
* prior written permission is obtained from KeeeX SAS.
|
|
23
|
+
*
|
|
24
|
+
* For a license agreement, please contact:
|
|
25
|
+
* <mailto: contact@keeex.net>
|
|
26
|
+
*
|
|
27
|
+
*/
|
|
28
28
|
import type * as envSrv from "./env.js";
|
|
29
29
|
import type * as types from "./types.js";
|
|
30
30
|
import type { JSONObject } from "@keeex/utils/json.js";
|
package/lib/services/loader.d.ts
CHANGED
|
@@ -1,30 +1,30 @@
|
|
|
1
1
|
/**
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
2
|
+
* @license
|
|
3
|
+
* @preserve
|
|
4
|
+
*
|
|
5
|
+
* KeeeX SAS Public code
|
|
6
|
+
* https://keeex.me
|
|
7
|
+
* Copyright 2013-2026 KeeeX All Rights Reserved.
|
|
8
|
+
*
|
|
9
|
+
* These computer program listings and specifications, herein,
|
|
10
|
+
* are and remain the property of KeeeX SAS. The intellectual
|
|
11
|
+
* and technical concepts herein are proprietary to KeeeX SAS
|
|
12
|
+
* and may be covered by EU and foreign patents,
|
|
13
|
+
* patents in process, trade secrets and copyright law.
|
|
14
|
+
*
|
|
15
|
+
* These listings are published as a way to provide third party
|
|
16
|
+
* with the ability to process KeeeX data.
|
|
17
|
+
* As such, support for public inquiries is limited.
|
|
18
|
+
* They are provided "as-is", without warrany of any kind.
|
|
19
|
+
*
|
|
20
|
+
* They shall not be reproduced or copied or used in whole or
|
|
21
|
+
* in part as the basis for manufacture or sale of items unless
|
|
22
|
+
* prior written permission is obtained from KeeeX SAS.
|
|
23
|
+
*
|
|
24
|
+
* For a license agreement, please contact:
|
|
25
|
+
* <mailto: contact@keeex.net>
|
|
26
|
+
*
|
|
27
|
+
*/
|
|
28
28
|
import * as envSrv from "./env.js";
|
|
29
29
|
import * as types from "./types.js";
|
|
30
30
|
/**
|
|
@@ -40,7 +40,7 @@ import * as types from "./types.js";
|
|
|
40
40
|
* @returns
|
|
41
41
|
* The config object (cached, if applicable)
|
|
42
42
|
*/
|
|
43
|
-
export declare const loadConfig: <ConfigType, SecretsType =
|
|
43
|
+
export declare const loadConfig: <ConfigType, SecretsType = undefined, TypedConfigType = ConfigType, TypedSecretsType = SecretsType>(loadingConfig?: types.ProjectLoadingConfig<ConfigType, SecretsType, TypedConfigType, TypedSecretsType>, argv?: Array<string>, env?: envSrv.SysEnv) => Promise<types.ProjectConfig<TypedConfigType, TypedSecretsType>>;
|
|
44
44
|
/**
|
|
45
45
|
* Internal function used for testing purpose.
|
|
46
46
|
*
|
package/lib/services/loader.js
CHANGED
|
@@ -26,6 +26,7 @@
|
|
|
26
26
|
*
|
|
27
27
|
*/
|
|
28
28
|
import * as nodeFs from "node:fs";
|
|
29
|
+
import * as nodePath from "node:path";
|
|
29
30
|
import { asError } from "@keeex/utils/error.js";
|
|
30
31
|
import * as utilsJson from "../utils/json.js";
|
|
31
32
|
import * as cliSrv from "./cli.js";
|
|
@@ -82,15 +83,37 @@ const getFromCliArgs = async (argv, baseDataConfig) => {
|
|
|
82
83
|
const fromCliFile = (propPath) => cliSrv.getCfgValue(argv, baseDataConfig.argType, propPath, true);
|
|
83
84
|
return getFromRecords(baseDataConfig, fromCliDirect, fromCliFile, "CLIARGS");
|
|
84
85
|
};
|
|
86
|
+
const getFromIndividualFiles = async (baseDataConfig) => {
|
|
87
|
+
const fileValues = baseDataConfig.fileValues;
|
|
88
|
+
if (!fileValues)
|
|
89
|
+
return null;
|
|
90
|
+
let res = {};
|
|
91
|
+
await Promise.all(Object.entries(fileValues.files).map(async ([filename, prop]) => {
|
|
92
|
+
const filepath = nodePath.resolve(fileValues.baseDirectory, filename);
|
|
93
|
+
if (!nodeFs.existsSync(filepath))
|
|
94
|
+
return;
|
|
95
|
+
const content = (await nodeFs.promises.readFile(filepath, "utf8")).trim();
|
|
96
|
+
const parsedContent = envSrv.parseValueFromEnvString(content, prop.type);
|
|
97
|
+
if (typeof prop.target === "string") {
|
|
98
|
+
utilsJson.setPathValue(res, prop.target, parsedContent);
|
|
99
|
+
}
|
|
100
|
+
else {
|
|
101
|
+
const temp = await prop.target(parsedContent);
|
|
102
|
+
res = utilsJson.mergeJsonDeep(res, temp);
|
|
103
|
+
}
|
|
104
|
+
}));
|
|
105
|
+
return res;
|
|
106
|
+
};
|
|
85
107
|
/** Build the specific object based on the available sources */
|
|
86
108
|
const getBaseData = async (argv, env, baseDataConfig) => {
|
|
87
|
-
const [fromFile, fromEnv, fromCliBase, fromCliArgs] = await Promise.all([
|
|
109
|
+
const [fromFile, fromIndividualFiles, fromEnv, fromCliBase, fromCliArgs] = await Promise.all([
|
|
88
110
|
getFromFile(env, baseDataConfig),
|
|
111
|
+
getFromIndividualFiles(baseDataConfig),
|
|
89
112
|
getFromEnv(env, baseDataConfig),
|
|
90
113
|
cliSrv.getFromCliBase(argv, baseDataConfig),
|
|
91
114
|
getFromCliArgs(argv, baseDataConfig),
|
|
92
115
|
]);
|
|
93
|
-
const result = utilsJson.mergeJsonDeep(baseDataConfig.defaultValues, fromFile, fromEnv, fromCliBase, fromCliArgs);
|
|
116
|
+
const result = utilsJson.mergeJsonDeep(baseDataConfig.defaultValues, fromFile, fromIndividualFiles, fromEnv, fromCliBase, fromCliArgs);
|
|
94
117
|
try {
|
|
95
118
|
baseDataConfig.predicate?.(result, true);
|
|
96
119
|
}
|
|
@@ -115,15 +138,16 @@ const getProjectConfig = async (argv, env, loadingConfig) => {
|
|
|
115
138
|
const secrets = await getBaseData(argv, env, {
|
|
116
139
|
argType: types.ArgType.secrets,
|
|
117
140
|
defaultValues: loadingConfig.secretsDefault ?? {},
|
|
118
|
-
environmentValues: loadingConfig.
|
|
141
|
+
environmentValues: loadingConfig.secretsEnvironmentVars ?? {},
|
|
142
|
+
fileValues: loadingConfig.secretFiles,
|
|
119
143
|
loadedFrom,
|
|
120
144
|
predicate: loadingConfig.secretsPredicate,
|
|
121
145
|
});
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
return merged;
|
|
146
|
+
const merged = (secrets ? { ...baseConfig, secrets } : baseConfig);
|
|
147
|
+
if (loadingConfig.convert) {
|
|
148
|
+
return await loadingConfig.convert(merged);
|
|
125
149
|
}
|
|
126
|
-
return
|
|
150
|
+
return merged;
|
|
127
151
|
}
|
|
128
152
|
catch (error) {
|
|
129
153
|
throw new Error(`Failure to load config (tried:${loadedFrom.map((c) => JSON.stringify(c)).join(",")})`, { cause: error });
|
|
@@ -157,8 +181,9 @@ export const loadConfig = (loadingConfig, argv, env) => {
|
|
|
157
181
|
const realArgv = argv ?? process.argv;
|
|
158
182
|
const realEnv = env ?? process.env;
|
|
159
183
|
const realConfig = loadingConfig ?? {};
|
|
160
|
-
if (argv || env)
|
|
184
|
+
if (argv || env) {
|
|
161
185
|
return getProjectConfig(realArgv, realEnv, realConfig);
|
|
186
|
+
}
|
|
162
187
|
configCache ??= loadConfigCached(realConfig, realArgv, realEnv);
|
|
163
188
|
return configCache;
|
|
164
189
|
};
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* @preserve
|
|
4
|
+
*
|
|
5
|
+
* KeeeX SAS Public code
|
|
6
|
+
* https://keeex.me
|
|
7
|
+
* Copyright 2013-2026 KeeeX All Rights Reserved.
|
|
8
|
+
*
|
|
9
|
+
* These computer program listings and specifications, herein,
|
|
10
|
+
* are and remain the property of KeeeX SAS. The intellectual
|
|
11
|
+
* and technical concepts herein are proprietary to KeeeX SAS
|
|
12
|
+
* and may be covered by EU and foreign patents,
|
|
13
|
+
* patents in process, trade secrets and copyright law.
|
|
14
|
+
*
|
|
15
|
+
* These listings are published as a way to provide third party
|
|
16
|
+
* with the ability to process KeeeX data.
|
|
17
|
+
* As such, support for public inquiries is limited.
|
|
18
|
+
* They are provided "as-is", without warrany of any kind.
|
|
19
|
+
*
|
|
20
|
+
* They shall not be reproduced or copied or used in whole or
|
|
21
|
+
* in part as the basis for manufacture or sale of items unless
|
|
22
|
+
* prior written permission is obtained from KeeeX SAS.
|
|
23
|
+
*
|
|
24
|
+
* For a license agreement, please contact:
|
|
25
|
+
* <mailto: contact@keeex.net>
|
|
26
|
+
*
|
|
27
|
+
*/
|
|
28
|
+
import type * as types from "./types.js";
|
|
29
|
+
interface ConfigSingleton<ConfigType, SecretsType> {
|
|
30
|
+
loadConfig: () => Promise<void>;
|
|
31
|
+
config: types.ProjectConfig<ConfigType, SecretsType>;
|
|
32
|
+
}
|
|
33
|
+
export declare const getConfigSingleton: <ConfigType extends object, SecretsType = undefined, TypedConfigType = ConfigType, TypedSecretsType = SecretsType>(params: types.ProjectLoadingConfig<ConfigType, SecretsType, TypedConfigType, TypedSecretsType>) => ConfigSingleton<TypedConfigType, TypedSecretsType>;
|
|
34
|
+
export {};
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* @preserve
|
|
4
|
+
*
|
|
5
|
+
* KeeeX SAS Public code
|
|
6
|
+
* https://keeex.me
|
|
7
|
+
* Copyright 2013-2026 KeeeX All Rights Reserved.
|
|
8
|
+
*
|
|
9
|
+
* These computer program listings and specifications, herein,
|
|
10
|
+
* are and remain the property of KeeeX SAS. The intellectual
|
|
11
|
+
* and technical concepts herein are proprietary to KeeeX SAS
|
|
12
|
+
* and may be covered by EU and foreign patents,
|
|
13
|
+
* patents in process, trade secrets and copyright law.
|
|
14
|
+
*
|
|
15
|
+
* These listings are published as a way to provide third party
|
|
16
|
+
* with the ability to process KeeeX data.
|
|
17
|
+
* As such, support for public inquiries is limited.
|
|
18
|
+
* They are provided "as-is", without warrany of any kind.
|
|
19
|
+
*
|
|
20
|
+
* They shall not be reproduced or copied or used in whole or
|
|
21
|
+
* in part as the basis for manufacture or sale of items unless
|
|
22
|
+
* prior written permission is obtained from KeeeX SAS.
|
|
23
|
+
*
|
|
24
|
+
* For a license agreement, please contact:
|
|
25
|
+
* <mailto: contact@keeex.net>
|
|
26
|
+
*
|
|
27
|
+
*/
|
|
28
|
+
import * as loader from "./loader.js";
|
|
29
|
+
export const getConfigSingleton = (params) => {
|
|
30
|
+
let loadedConfig = null;
|
|
31
|
+
let loadConfigCache = null;
|
|
32
|
+
/**
|
|
33
|
+
* Load the project configuration.
|
|
34
|
+
*
|
|
35
|
+
* This function can be called multiple time, only one loading will occur.
|
|
36
|
+
*/
|
|
37
|
+
const loadConfig = () => {
|
|
38
|
+
loadConfigCache ??= (async () => {
|
|
39
|
+
try {
|
|
40
|
+
loadedConfig = await loader.loadConfig(params);
|
|
41
|
+
}
|
|
42
|
+
catch (error) {
|
|
43
|
+
loadConfigCache = null;
|
|
44
|
+
throw error;
|
|
45
|
+
}
|
|
46
|
+
})();
|
|
47
|
+
return loadConfigCache;
|
|
48
|
+
};
|
|
49
|
+
/**
|
|
50
|
+
* Access the configuration object when loaded.
|
|
51
|
+
*
|
|
52
|
+
* Trying to read a property on this object before loading the configuration will raise an
|
|
53
|
+
* exception.
|
|
54
|
+
*/
|
|
55
|
+
const config = new Proxy({}, {
|
|
56
|
+
get: (_target, prop) => {
|
|
57
|
+
if (loadedConfig === null)
|
|
58
|
+
throw new Error("Configuration was not loaded");
|
|
59
|
+
return loadedConfig[prop];
|
|
60
|
+
},
|
|
61
|
+
});
|
|
62
|
+
return { config: config, loadConfig };
|
|
63
|
+
};
|
package/lib/services/types.d.ts
CHANGED
|
@@ -25,7 +25,8 @@
|
|
|
25
25
|
* <mailto: contact@keeex.net>
|
|
26
26
|
*
|
|
27
27
|
*/
|
|
28
|
-
import type
|
|
28
|
+
import { type JSONObject } from "@keeex/utils/json.js";
|
|
29
|
+
import type * as utilsTypes from "@keeex/utils/types/types.js";
|
|
29
30
|
export type BaseConfig = Record<string, any>;
|
|
30
31
|
/** Separate config from secret inputs */
|
|
31
32
|
export declare enum ArgType {
|
|
@@ -46,24 +47,90 @@ export interface BaseDataConfig<Type> {
|
|
|
46
47
|
argType: ArgType | string;
|
|
47
48
|
defaultValues: DeepPartial<Type>;
|
|
48
49
|
environmentValues: EnvVarsList;
|
|
50
|
+
fileValues?: SecretFiles;
|
|
49
51
|
loadedFrom: Array<string>;
|
|
50
|
-
predicate?: TypePredicate<Type>;
|
|
52
|
+
predicate?: utilsTypes.TypePredicate<Type>;
|
|
51
53
|
}
|
|
52
|
-
export
|
|
54
|
+
export type ConvertFunc<ConfigType, SecretsType, TypedConfigType, TypedSecretsType> = (raw: ProjectConfig<ConfigType, SecretsType>) => utilsTypes.Awaitable<ProjectConfig<TypedConfigType, TypedSecretsType>>;
|
|
55
|
+
export type SecretTransform<InType> = (input: InType) => utilsTypes.Awaitable<JSONObject>;
|
|
56
|
+
/** Definition of a single file to load a secret from */
|
|
57
|
+
interface SecretFileString {
|
|
58
|
+
/** Will parse the file as if read from an environment variable */
|
|
59
|
+
type: TypeForEnv.string;
|
|
60
|
+
/**
|
|
61
|
+
* If a function return a JSON object to merge with secrets.
|
|
62
|
+
*
|
|
63
|
+
* If a string, the path to a property to fill.
|
|
64
|
+
*/
|
|
65
|
+
target: SecretTransform<string> | string;
|
|
66
|
+
}
|
|
67
|
+
interface SecretFileBoolean {
|
|
68
|
+
/** Will parse the file as if read from an environment variable */
|
|
69
|
+
type: TypeForEnv.boolean;
|
|
70
|
+
/**
|
|
71
|
+
* If a function return a JSON object to merge with secrets.
|
|
72
|
+
*
|
|
73
|
+
* If a string, the path to a property to fill.
|
|
74
|
+
*/
|
|
75
|
+
target: SecretTransform<boolean> | string;
|
|
76
|
+
}
|
|
77
|
+
interface SecretFileNumber {
|
|
78
|
+
/** Will parse the file as if read from an environment variable */
|
|
79
|
+
type: TypeForEnv.number;
|
|
80
|
+
/**
|
|
81
|
+
* If a function return a JSON object to merge with secrets.
|
|
82
|
+
*
|
|
83
|
+
* If a string, the path to a property to fill.
|
|
84
|
+
*/
|
|
85
|
+
target: SecretTransform<number> | string;
|
|
86
|
+
}
|
|
87
|
+
interface SecretFileJson {
|
|
88
|
+
/** Will parse the file as if read from an environment variable */
|
|
89
|
+
type: TypeForEnv.json;
|
|
90
|
+
/**
|
|
91
|
+
* If a function return a JSON object to merge with secrets.
|
|
92
|
+
*
|
|
93
|
+
* If a string, the path to a property to fill.
|
|
94
|
+
*/
|
|
95
|
+
target: SecretTransform<JSONObject> | string;
|
|
96
|
+
}
|
|
97
|
+
type SecretFile = SecretFileString | SecretFileBoolean | SecretFileNumber | SecretFileJson;
|
|
98
|
+
/** Definition of files to load secrets from */
|
|
99
|
+
interface SecretFiles {
|
|
100
|
+
baseDirectory: string;
|
|
101
|
+
files: Record<string, SecretFile>;
|
|
102
|
+
}
|
|
103
|
+
export interface ProjectLoadingConfig<ConfigType, SecretsType = undefined, TypedConfigType = ConfigType, TypedSecretsType = SecretsType> {
|
|
53
104
|
/** Default values to be used as the base of all configs */
|
|
54
105
|
configDefault?: DeepPartial<ConfigType>;
|
|
55
106
|
/** List properties that can be read from environment variables for the config */
|
|
56
107
|
configEnvironmentVars?: EnvVarsList;
|
|
57
|
-
/**
|
|
58
|
-
|
|
108
|
+
/**
|
|
109
|
+
* Type predicate for the base configuration (without secrets).
|
|
110
|
+
*
|
|
111
|
+
* Note that this applies to the loaded secret, regardless of the behavior of the `convert`
|
|
112
|
+
* function.
|
|
113
|
+
*/
|
|
114
|
+
configPredicate?: utilsTypes.TypePredicate<ConfigType>;
|
|
115
|
+
/** Convert from the raw types to custom types */
|
|
116
|
+
convert?: ConvertFunc<ConfigType, SecretsType, TypedConfigType, TypedSecretsType>;
|
|
59
117
|
/** Default values to be used as the base of all secrets */
|
|
60
118
|
secretsDefault?: DeepPartial<SecretsType>;
|
|
61
119
|
/** List properties that can be read from environment variables for the secret */
|
|
62
|
-
|
|
63
|
-
/**
|
|
64
|
-
|
|
120
|
+
secretsEnvironmentVars?: EnvVarsList;
|
|
121
|
+
/** Load from files in a specific directory */
|
|
122
|
+
secretFiles?: SecretFiles;
|
|
123
|
+
/**
|
|
124
|
+
* Type predicate for the secrets only.
|
|
125
|
+
*
|
|
126
|
+
* Note that this applies to the loaded secret, regardless of the behavior of the `convert`
|
|
127
|
+
* function.
|
|
128
|
+
*/
|
|
129
|
+
secretsPredicate?: SecretsType extends never ? never : utilsTypes.TypePredicate<SecretsType>;
|
|
65
130
|
}
|
|
66
|
-
export type ProjectConfig<ConfigType, SecretsType =
|
|
131
|
+
export type ProjectConfig<ConfigType, SecretsType = undefined> = SecretsType extends undefined ? {
|
|
132
|
+
[key in keyof ConfigType]: ConfigType[key];
|
|
133
|
+
} : {
|
|
67
134
|
[key in keyof ConfigType]: ConfigType[key];
|
|
68
135
|
} & {
|
|
69
136
|
secrets: SecretsType;
|
package/package.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"name":"@keeex/projectconfig","version":"
|
|
1
|
+
{"name":"@keeex/projectconfig","version":"7.0.0","description":"Load project configuration with separate secrets","main":"./lib/index.js","scripts":{},"type":"module","author":"KeeeX SAS","contributors":[{"email":"gabriel@keeex.net","name":"Gabriel Paul \"Cley Faye\" Risterucci"}],"license":"SEE LICENSE IN LICENSE","files":["lib"],"exports":{".":"./lib/index.js","./index.js":"./lib/index.js"},"dependencies":{"@keeex/utils":"^7.6.2","@keeex/utils-node":"^6.9.2"},"allowScripts":{"@swc/core@1.15.41":true,"unrs-resolver":false},"homepage":"https://keeex.me/oss"}
|