@achs/env 2.0.0 → 3.1.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/.eslintignore +3 -0
- package/.eslintrc.json +329 -0
- package/.vscode/extensions.json +18 -0
- package/.vscode/launch.json +30 -0
- package/.vscode/settings.json +29 -0
- package/CHANGELOG.md +13 -0
- package/README.md +14 -7
- package/jest.config.json +28 -0
- package/package.json +16 -16
- package/{arguments.js → src/arguments.ts} +38 -14
- package/src/commands/env.command.ts +139 -0
- package/src/commands/export.command.ts +88 -0
- package/{commands/index.d.ts → src/commands/index.ts} +0 -1
- package/src/commands/pull.command.ts +52 -0
- package/src/commands/push.command.ts +48 -0
- package/src/commands/schema.command.ts +31 -0
- package/src/exec.ts +221 -0
- package/{index.d.ts → src/index.ts} +0 -1
- package/{interfaces/index.d.ts → src/interfaces/index.ts} +0 -1
- package/src/interfaces/loader.interface.ts +66 -0
- package/src/main.ts +6 -0
- package/src/providers/app-settings.provider.ts +67 -0
- package/src/providers/azure-key-vault.provider.ts +277 -0
- package/src/providers/index.ts +29 -0
- package/src/providers/local.provider.ts +44 -0
- package/src/providers/package-json.provider.ts +39 -0
- package/src/utils/command.util.ts +223 -0
- package/{utils/index.d.ts → src/utils/index.ts} +0 -1
- package/src/utils/interpolate.util.ts +65 -0
- package/src/utils/json.util.ts +116 -0
- package/{utils/logger.js → src/utils/logger.ts} +6 -6
- package/src/utils/normalize.util.ts +142 -0
- package/src/utils/schema.util.ts +191 -0
- package/tests/env/appsettings.json +32 -0
- package/tests/env/dev.env.json +12 -0
- package/tests/env/dev.local.env.json +9 -0
- package/tests/env/env.schema.json +225 -0
- package/tests/env/keys.json +7 -0
- package/tests/env/settings/schema.json +239 -0
- package/tests/env/settings/settings.json +22 -0
- package/tests/env.int.test.ts +42 -0
- package/tests/exec.ts +19 -0
- package/tests/export.int.test.ts +9 -0
- package/tests/pull-push.int.test.ts +15 -0
- package/tests/run.js +32 -0
- package/tests/schema.int.test.ts +9 -0
- package/tests/setup.ts +13 -0
- package/tsconfig.build.json +10 -0
- package/tsconfig.json +37 -0
- package/arguments.d.ts +0 -25
- package/arguments.d.ts.map +0 -1
- package/arguments.js.map +0 -1
- package/commands/env.command.d.ts +0 -8
- package/commands/env.command.d.ts.map +0 -1
- package/commands/env.command.js +0 -85
- package/commands/env.command.js.map +0 -1
- package/commands/export.command.d.ts +0 -8
- package/commands/export.command.d.ts.map +0 -1
- package/commands/export.command.js +0 -54
- package/commands/export.command.js.map +0 -1
- package/commands/index.d.ts.map +0 -1
- package/commands/index.js +0 -14
- package/commands/index.js.map +0 -1
- package/commands/pull.command.d.ts +0 -7
- package/commands/pull.command.d.ts.map +0 -1
- package/commands/pull.command.js +0 -39
- package/commands/pull.command.js.map +0 -1
- package/commands/push.command.d.ts +0 -7
- package/commands/push.command.d.ts.map +0 -1
- package/commands/push.command.js +0 -38
- package/commands/push.command.js.map +0 -1
- package/commands/schema.command.d.ts +0 -4
- package/commands/schema.command.d.ts.map +0 -1
- package/commands/schema.command.js +0 -18
- package/commands/schema.command.js.map +0 -1
- package/exec.d.ts +0 -3
- package/exec.d.ts.map +0 -1
- package/exec.js +0 -142
- package/exec.js.map +0 -1
- package/index.d.ts.map +0 -1
- package/index.js +0 -20
- package/index.js.map +0 -1
- package/interfaces/index.d.ts.map +0 -1
- package/interfaces/index.js +0 -18
- package/interfaces/index.js.map +0 -1
- package/interfaces/loader.interface.d.ts +0 -21
- package/interfaces/loader.interface.d.ts.map +0 -1
- package/interfaces/loader.interface.js +0 -3
- package/interfaces/loader.interface.js.map +0 -1
- package/main.d.ts +0 -3
- package/main.d.ts.map +0 -1
- package/main.js +0 -6
- package/main.js.map +0 -1
- package/providers/app-settings.provider.d.ts +0 -8
- package/providers/app-settings.provider.d.ts.map +0 -1
- package/providers/app-settings.provider.js +0 -50
- package/providers/app-settings.provider.js.map +0 -1
- package/providers/azure-key-vault.provider.d.ts +0 -22
- package/providers/azure-key-vault.provider.d.ts.map +0 -1
- package/providers/azure-key-vault.provider.js +0 -164
- package/providers/azure-key-vault.provider.js.map +0 -1
- package/providers/index.d.ts +0 -8
- package/providers/index.d.ts.map +0 -1
- package/providers/index.js +0 -28
- package/providers/index.js.map +0 -1
- package/providers/package-json.provider.d.ts +0 -8
- package/providers/package-json.provider.d.ts.map +0 -1
- package/providers/package-json.provider.js +0 -29
- package/providers/package-json.provider.js.map +0 -1
- package/tsconfig.build.tsbuildinfo +0 -1
- package/utils/command.util.d.ts +0 -13
- package/utils/command.util.d.ts.map +0 -1
- package/utils/command.util.js +0 -134
- package/utils/command.util.js.map +0 -1
- package/utils/index.d.ts.map +0 -1
- package/utils/index.js +0 -23
- package/utils/index.js.map +0 -1
- package/utils/interpolate.util.d.ts +0 -4
- package/utils/interpolate.util.d.ts.map +0 -1
- package/utils/interpolate.util.js +0 -33
- package/utils/interpolate.util.js.map +0 -1
- package/utils/json.util.d.ts +0 -5
- package/utils/json.util.d.ts.map +0 -1
- package/utils/json.util.js +0 -48
- package/utils/json.util.js.map +0 -1
- package/utils/logger.d.ts +0 -3
- package/utils/logger.d.ts.map +0 -1
- package/utils/logger.js.map +0 -1
- package/utils/normalize.util.d.ts +0 -3
- package/utils/normalize.util.d.ts.map +0 -1
- package/utils/normalize.util.js +0 -61
- package/utils/normalize.util.js.map +0 -1
- package/utils/schema.util.d.ts +0 -11
- package/utils/schema.util.d.ts.map +0 -1
- package/utils/schema.util.js +0 -100
- package/utils/schema.util.js.map +0 -1
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import merge from 'merge-deep';
|
|
3
|
+
import { Arguments } from 'yargs';
|
|
4
|
+
import { CommandArguments } from 'arguments';
|
|
5
|
+
import { EnvCommandArguments } from 'commands/env.command';
|
|
6
|
+
import { EnvProviderConfig, EnvProviderResult } from '../interfaces';
|
|
7
|
+
import {
|
|
8
|
+
createValidators,
|
|
9
|
+
interpolate,
|
|
10
|
+
logger,
|
|
11
|
+
readJson,
|
|
12
|
+
schemaFrom,
|
|
13
|
+
writeJson
|
|
14
|
+
} from '../utils';
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Injects config to command arguments from file.
|
|
18
|
+
*
|
|
19
|
+
* @param {Record<string, unknown>} argv
|
|
20
|
+
* @param {[string, string]} delimiters
|
|
21
|
+
*/
|
|
22
|
+
export async function loadConfigFile(
|
|
23
|
+
argv: Record<string, unknown>,
|
|
24
|
+
delimiters: [string, string]
|
|
25
|
+
): Promise<void> {
|
|
26
|
+
if (typeof argv.configFile === 'string') {
|
|
27
|
+
const path = interpolate(argv.configFile, argv, delimiters);
|
|
28
|
+
const [config, success] = await readJson(path);
|
|
29
|
+
|
|
30
|
+
if (success) for (const key in config) argv[key] ??= config[key];
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Extracts subcommand from command line parameters.
|
|
36
|
+
*
|
|
37
|
+
* @export
|
|
38
|
+
* @param {string[]} rawArgv process.argv.slice(2)
|
|
39
|
+
* @param {[string, string]} delimiters
|
|
40
|
+
*
|
|
41
|
+
* @returns {string[]} subcommand for wrap if exists
|
|
42
|
+
*/
|
|
43
|
+
export function getSubcommand(rawArgv: string[], delimiters: [string, string]) {
|
|
44
|
+
let subcommand: string[] = [];
|
|
45
|
+
|
|
46
|
+
// subcommand delimiter indexes
|
|
47
|
+
const begin = rawArgv.indexOf(delimiters[0]);
|
|
48
|
+
const count = rawArgv.lastIndexOf(delimiters[1]) - begin;
|
|
49
|
+
|
|
50
|
+
// calculates subcommand surrounded by delimiters
|
|
51
|
+
if (begin > 0) {
|
|
52
|
+
subcommand =
|
|
53
|
+
count > 0
|
|
54
|
+
? rawArgv.splice(begin, count + 1).slice(1, -1)
|
|
55
|
+
: rawArgv.splice(begin).slice(1);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return subcommand;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Loads providers JSON schema from file.
|
|
63
|
+
*
|
|
64
|
+
* @param {Record<string, unknown>} argv
|
|
65
|
+
* @param {[string, string]} delimiters
|
|
66
|
+
*
|
|
67
|
+
* @returns {Promise<Record<string, unknown>>}
|
|
68
|
+
*/
|
|
69
|
+
export async function loadSchemaFile(
|
|
70
|
+
argv: Record<string, unknown>,
|
|
71
|
+
delimiters: [string, string]
|
|
72
|
+
): Promise<Record<string, unknown> | undefined> {
|
|
73
|
+
if (typeof argv.schemaFile === 'string') {
|
|
74
|
+
const path = interpolate(argv.schemaFile, argv, delimiters);
|
|
75
|
+
const [schema, success] = await readJson(path);
|
|
76
|
+
|
|
77
|
+
return success ? schema : undefined;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
return undefined;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Reads project package.json.
|
|
85
|
+
*
|
|
86
|
+
* @export
|
|
87
|
+
* @returns {Promise<Record<string, unknown>> | never}
|
|
88
|
+
*/
|
|
89
|
+
export async function loadProjectInfo(): Promise<
|
|
90
|
+
Record<string, unknown> | undefined
|
|
91
|
+
> {
|
|
92
|
+
try {
|
|
93
|
+
return await import(`${process.cwd()}/package.json`);
|
|
94
|
+
} catch {
|
|
95
|
+
logger.warn(
|
|
96
|
+
`project file ${chalk.underline.yellow('package.json')} not found`
|
|
97
|
+
);
|
|
98
|
+
|
|
99
|
+
return undefined;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Executes load functions from provider handlers.
|
|
105
|
+
*
|
|
106
|
+
* @param {EnvProviderConfig[]} providers
|
|
107
|
+
* @param {Partial<Arguments<EnvCommandArguments>>} argv
|
|
108
|
+
*
|
|
109
|
+
* @returns {EnvProviderResult[]}
|
|
110
|
+
*/
|
|
111
|
+
export function loadVariablesFromProviders(
|
|
112
|
+
providers: EnvProviderConfig[],
|
|
113
|
+
argv: Partial<Arguments<EnvCommandArguments>>
|
|
114
|
+
): Promise<EnvProviderResult[]> {
|
|
115
|
+
if (!providers) return Promise.resolve([]) as Promise<EnvProviderResult[]>;
|
|
116
|
+
|
|
117
|
+
return Promise.all(
|
|
118
|
+
providers.map(({ handler: { key, load }, config }) => {
|
|
119
|
+
logger.silly(`executing ${chalk.yellow(key)} provider`);
|
|
120
|
+
|
|
121
|
+
const value = load(argv, config);
|
|
122
|
+
|
|
123
|
+
if (value instanceof Promise) {
|
|
124
|
+
return value.then((value) => ({
|
|
125
|
+
key,
|
|
126
|
+
config,
|
|
127
|
+
value
|
|
128
|
+
}));
|
|
129
|
+
} else {
|
|
130
|
+
return { key, config, value };
|
|
131
|
+
}
|
|
132
|
+
})
|
|
133
|
+
);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Flattern environment provider results.
|
|
138
|
+
*
|
|
139
|
+
* @param {EnvProviderResult[]} results
|
|
140
|
+
* @param {Partial<Arguments<EnvCommandArguments>>} argv
|
|
141
|
+
*
|
|
142
|
+
* @throws {Error} on schema validation failed
|
|
143
|
+
*
|
|
144
|
+
* @returns {EnvProviderResult[]} flatten results
|
|
145
|
+
*/
|
|
146
|
+
export function flatResults(
|
|
147
|
+
results: EnvProviderResult[]
|
|
148
|
+
): EnvProviderResult[] | never {
|
|
149
|
+
return results.flatMap(({ value }) => {
|
|
150
|
+
if (Array.isArray(value)) value = merge({}, ...value);
|
|
151
|
+
|
|
152
|
+
return value;
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Flattern and validates environment provider results.
|
|
158
|
+
*
|
|
159
|
+
* @param {EnvProviderResult[]} results
|
|
160
|
+
* @param {Partial<Arguments<EnvCommandArguments>>} argv
|
|
161
|
+
*
|
|
162
|
+
* @throws {Error} on schema validation failed
|
|
163
|
+
*
|
|
164
|
+
* @returns {EnvProviderResult[]}
|
|
165
|
+
*/
|
|
166
|
+
export function flatAndValidateResults(
|
|
167
|
+
results: EnvProviderResult[],
|
|
168
|
+
argv: Partial<Arguments<EnvCommandArguments>>
|
|
169
|
+
): EnvProviderResult[] | never {
|
|
170
|
+
if (!argv.schemaValidate) return flatResults(results);
|
|
171
|
+
|
|
172
|
+
const validators = createValidators(argv.schema!, argv.detectFormat);
|
|
173
|
+
|
|
174
|
+
return results.flatMap(({ key, value }) => {
|
|
175
|
+
if (Array.isArray(value)) value = merge({}, ...value);
|
|
176
|
+
|
|
177
|
+
const validator = validators![key];
|
|
178
|
+
|
|
179
|
+
if (validator(value)) return value;
|
|
180
|
+
|
|
181
|
+
logger.error(
|
|
182
|
+
`schema validation failed for ${chalk.yellow(key)}`,
|
|
183
|
+
validator.errors
|
|
184
|
+
);
|
|
185
|
+
|
|
186
|
+
throw new Error(`schema validation failed for ${key}`);
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Creates or updates JSON schema from
|
|
192
|
+
* environment variables grouped by provider key.
|
|
193
|
+
*
|
|
194
|
+
* @export
|
|
195
|
+
* @param {EnvProviderResult[]} env
|
|
196
|
+
* @param {Arguments<EnvCommandArguments>} argv
|
|
197
|
+
*
|
|
198
|
+
* @returns {Promise<object>} JSON schema grouped by provider key.
|
|
199
|
+
*/
|
|
200
|
+
export async function generateSchemaFrom(
|
|
201
|
+
env: EnvProviderResult[],
|
|
202
|
+
argv: Arguments<CommandArguments>
|
|
203
|
+
): Promise<object> {
|
|
204
|
+
const { resolve, nullable, detectFormat, schemaFile } = argv;
|
|
205
|
+
|
|
206
|
+
// generates schemas from proviers results
|
|
207
|
+
let schema = env.reduce((schema, { key, value }) => {
|
|
208
|
+
const env = Array.isArray(value) ? merge({}, ...value) : value;
|
|
209
|
+
|
|
210
|
+
schema[key] = schemaFrom(env, {
|
|
211
|
+
nullable,
|
|
212
|
+
strings: { detectFormat }
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
return schema;
|
|
216
|
+
}, {} as Record<string, unknown>);
|
|
217
|
+
|
|
218
|
+
if (resolve === 'merge') schema = merge(argv.schema, schema);
|
|
219
|
+
|
|
220
|
+
await writeJson(schemaFile, schema, true);
|
|
221
|
+
|
|
222
|
+
return schema;
|
|
223
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import subslate from 'subslate';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Validates value must be a record.
|
|
5
|
+
*
|
|
6
|
+
* @export
|
|
7
|
+
* @param {unknown} obj
|
|
8
|
+
* @returns {*} {obj is Record<string, unknown>}
|
|
9
|
+
*/
|
|
10
|
+
export function isRecord(obj: unknown): obj is Record<string, unknown> {
|
|
11
|
+
if (!obj || typeof obj !== 'object') return false;
|
|
12
|
+
|
|
13
|
+
return Object.keys(obj).length > 0;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Replaces string arguments with regex interpolation.
|
|
18
|
+
*
|
|
19
|
+
* @export
|
|
20
|
+
* @template T
|
|
21
|
+
* @param {T} value
|
|
22
|
+
* @param {Record<string, unknown>} args
|
|
23
|
+
* @param {[string, string]} [delimiters=['[[', ']]']]
|
|
24
|
+
*
|
|
25
|
+
* @returns {T} mutated value
|
|
26
|
+
*/
|
|
27
|
+
export function interpolate<T extends string | unknown>(
|
|
28
|
+
value: T,
|
|
29
|
+
args: Record<string, unknown>,
|
|
30
|
+
delimiters: [string, string] = ['[[', ']]']
|
|
31
|
+
): T {
|
|
32
|
+
if (typeof value === 'string') {
|
|
33
|
+
return subslate(value, args, {
|
|
34
|
+
startStopPairs: delimiters
|
|
35
|
+
}) as T;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
if (Array.isArray(value))
|
|
39
|
+
return value.map((a) => interpolate(a, args, delimiters)) as T;
|
|
40
|
+
|
|
41
|
+
if (isRecord(value)) return interpolateJson(value, args, delimiters) as T;
|
|
42
|
+
|
|
43
|
+
return value;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Replaces JSON string arguments with regex interpolation.
|
|
48
|
+
*
|
|
49
|
+
* @export
|
|
50
|
+
* @param {Record<string, unknown>} args
|
|
51
|
+
* @param {Record<string, unknown>} values
|
|
52
|
+
* @param {[string, string]} [delimiters=['[[', ']]']]
|
|
53
|
+
*
|
|
54
|
+
* @returns {Record<string, unknown>} mutated args
|
|
55
|
+
*/
|
|
56
|
+
export function interpolateJson(
|
|
57
|
+
values: Record<string, unknown>,
|
|
58
|
+
args: Record<string, unknown>,
|
|
59
|
+
delimiters: [string, string] = ['[[', ']]']
|
|
60
|
+
): Record<string, unknown> {
|
|
61
|
+
for (const key in values)
|
|
62
|
+
values[key] = interpolate(values[key], args, delimiters);
|
|
63
|
+
|
|
64
|
+
return values;
|
|
65
|
+
}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import os from 'os';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import { existsSync } from 'fs';
|
|
4
|
+
import { readFile, writeFile } from 'fs/promises';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Replaces undefined by null in JSON.stringify()
|
|
8
|
+
*
|
|
9
|
+
* @param {string} _ property key
|
|
10
|
+
* @param {any} value property value
|
|
11
|
+
*
|
|
12
|
+
* @returns {any} value
|
|
13
|
+
*/
|
|
14
|
+
const replacer = (_: string, value: any): typeof value | null =>
|
|
15
|
+
value === undefined ? null : value;
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Resolve a relative path for os.
|
|
19
|
+
*
|
|
20
|
+
* @export
|
|
21
|
+
* @param {string} filePath relative path from project root
|
|
22
|
+
*
|
|
23
|
+
* @returns {string} path
|
|
24
|
+
*/
|
|
25
|
+
export function resolvePath(filePath: string): string {
|
|
26
|
+
const home = os.homedir();
|
|
27
|
+
|
|
28
|
+
if (home !== undefined)
|
|
29
|
+
filePath = filePath.replace(/^~($|\/|\\)/, `${home}$1`);
|
|
30
|
+
|
|
31
|
+
return path.resolve(process.cwd(), filePath);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Reads and parses a JSON file.
|
|
36
|
+
*
|
|
37
|
+
* @export
|
|
38
|
+
* @template T
|
|
39
|
+
* @param {string} path
|
|
40
|
+
*
|
|
41
|
+
* @returns {Promise<[Record<string, any>, boolean]>}
|
|
42
|
+
*/
|
|
43
|
+
export async function readJson<T = Record<string, any>>(
|
|
44
|
+
path: string
|
|
45
|
+
): Promise<[T | Record<string, any>, boolean] | never> {
|
|
46
|
+
if (!existsSync(path)) return [{}, false];
|
|
47
|
+
|
|
48
|
+
return [JSON.parse(await readFile(path, 'utf8')), true];
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Saves a JSON into a file.
|
|
53
|
+
*
|
|
54
|
+
* @export
|
|
55
|
+
* @param {string} path
|
|
56
|
+
* @param {unknown} content
|
|
57
|
+
* @param {false} overwrite
|
|
58
|
+
* @param {false} undefinedAsNull replaces undefined by null
|
|
59
|
+
*
|
|
60
|
+
* @returns {Promise<boolean>}
|
|
61
|
+
*/
|
|
62
|
+
export async function writeJson(
|
|
63
|
+
path: string,
|
|
64
|
+
content: Record<string, unknown>,
|
|
65
|
+
overwrite = false,
|
|
66
|
+
undefinedAsNull = false
|
|
67
|
+
): Promise<boolean | never> {
|
|
68
|
+
const exists = existsSync(path);
|
|
69
|
+
|
|
70
|
+
if (exists && !overwrite) return false;
|
|
71
|
+
|
|
72
|
+
await writeFile(
|
|
73
|
+
path,
|
|
74
|
+
`${JSON.stringify(
|
|
75
|
+
content,
|
|
76
|
+
undefinedAsNull ? replacer : undefined,
|
|
77
|
+
4
|
|
78
|
+
)}\n`,
|
|
79
|
+
'utf8'
|
|
80
|
+
);
|
|
81
|
+
|
|
82
|
+
return true;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Saves a JSON into a file as dotenv.
|
|
87
|
+
*
|
|
88
|
+
* @export
|
|
89
|
+
* @param {string} path
|
|
90
|
+
* @param {unknown} content
|
|
91
|
+
* @param {false} overwrite
|
|
92
|
+
*
|
|
93
|
+
* @returns {Promise<boolean>}
|
|
94
|
+
*/
|
|
95
|
+
export async function writeEnvFromJson(
|
|
96
|
+
path: string,
|
|
97
|
+
content: Record<string, unknown>,
|
|
98
|
+
overwrite = false
|
|
99
|
+
): Promise<boolean | never> {
|
|
100
|
+
const exists = existsSync(path);
|
|
101
|
+
|
|
102
|
+
if (exists && !overwrite) return false;
|
|
103
|
+
|
|
104
|
+
let data = '';
|
|
105
|
+
|
|
106
|
+
for (const key in content) {
|
|
107
|
+
let value = content[key];
|
|
108
|
+
if (typeof value === 'string') value = `"${value}"`;
|
|
109
|
+
|
|
110
|
+
data += `${key}=${value}\n`;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
await writeFile(path, data, 'utf8');
|
|
114
|
+
|
|
115
|
+
return true;
|
|
116
|
+
}
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
1
|
+
import { LoggerWithoutCallSite as Logger } from 'tslog';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Global stdout wrap.
|
|
5
|
+
*/
|
|
6
|
+
export const logger = new Logger({
|
|
6
7
|
displayDateTime: true,
|
|
7
8
|
displayLoggerName: false,
|
|
8
9
|
displayInstanceName: false,
|
|
@@ -15,4 +16,3 @@ exports.logger = new tslog_1.LoggerWithoutCallSite({
|
|
|
15
16
|
dateTimePattern: 'hour:minute:second.millisecond',
|
|
16
17
|
maskPlaceholder: '***'
|
|
17
18
|
});
|
|
18
|
-
//# sourceMappingURL=logger.js.map
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Flatten a object keeping depth path
|
|
3
|
+
* in key using __ as level separator.
|
|
4
|
+
*
|
|
5
|
+
* @param {Record<string, any>} obj
|
|
6
|
+
* @param {string} nestingDelimiter char for delimit nesting levels
|
|
7
|
+
* @param {boolean} arrayDescomposition serialize or break down arrays
|
|
8
|
+
* @param {boolean} clearNull if null should removed from object
|
|
9
|
+
* @param {string} pkey first level key
|
|
10
|
+
*
|
|
11
|
+
* @returns {Record<string, string>} flattended object
|
|
12
|
+
*/
|
|
13
|
+
export function flatten(
|
|
14
|
+
obj: Record<string, any>,
|
|
15
|
+
nestingDelimiter = '__',
|
|
16
|
+
pkey = ''
|
|
17
|
+
): Record<string, string> {
|
|
18
|
+
const flattened: Record<string, string> = {};
|
|
19
|
+
|
|
20
|
+
for (let key in obj) {
|
|
21
|
+
const value = obj[key];
|
|
22
|
+
const type = typeof value;
|
|
23
|
+
|
|
24
|
+
if (value === undefined || type === 'function') continue;
|
|
25
|
+
|
|
26
|
+
// skipped property
|
|
27
|
+
if (key[0] === '#') continue;
|
|
28
|
+
key = pkey + key;
|
|
29
|
+
|
|
30
|
+
if (value === null || type !== 'object' || Array.isArray(value)) {
|
|
31
|
+
flattened[key] = value;
|
|
32
|
+
|
|
33
|
+
continue;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
Object.assign(
|
|
37
|
+
flattened,
|
|
38
|
+
flatten(value, nestingDelimiter, `${key}${nestingDelimiter}`)
|
|
39
|
+
);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return flattened;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Normalizes env object, converts arrays in list strings,
|
|
47
|
+
* only primitives types array,
|
|
48
|
+
* and removes $ global character from keys.
|
|
49
|
+
*
|
|
50
|
+
* @param {Record<string, any>} obj
|
|
51
|
+
* @param {string} nestingDelimiter char for delimit nesting levels
|
|
52
|
+
* @param {boolean} arrayDescomposition serialize or break down arrays
|
|
53
|
+
* @param {string} pkey first level key
|
|
54
|
+
*
|
|
55
|
+
* @returns {Record<string, string>} normalized object
|
|
56
|
+
*/
|
|
57
|
+
export function normalize(
|
|
58
|
+
obj: Record<string, any>,
|
|
59
|
+
nestingDelimiter = '__',
|
|
60
|
+
arrayDescomposition = false,
|
|
61
|
+
pkey = ''
|
|
62
|
+
): Record<string, string> {
|
|
63
|
+
const flattened: Record<string, string> = {};
|
|
64
|
+
|
|
65
|
+
for (let key in obj) {
|
|
66
|
+
const value = obj[key];
|
|
67
|
+
const type = typeof value;
|
|
68
|
+
|
|
69
|
+
if (value === null || value === undefined || type === 'function')
|
|
70
|
+
continue;
|
|
71
|
+
|
|
72
|
+
// global property, but prefix removed for injection
|
|
73
|
+
key = pkey + key.replace('$', '');
|
|
74
|
+
|
|
75
|
+
if (type !== 'object') {
|
|
76
|
+
flattened[key] = value;
|
|
77
|
+
|
|
78
|
+
continue;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
if (Array.isArray(value)) {
|
|
82
|
+
normalizeArray(
|
|
83
|
+
flattened,
|
|
84
|
+
key,
|
|
85
|
+
value,
|
|
86
|
+
nestingDelimiter,
|
|
87
|
+
arrayDescomposition
|
|
88
|
+
);
|
|
89
|
+
} else {
|
|
90
|
+
Object.assign(
|
|
91
|
+
flattened,
|
|
92
|
+
normalize(
|
|
93
|
+
value,
|
|
94
|
+
nestingDelimiter,
|
|
95
|
+
arrayDescomposition,
|
|
96
|
+
`${key}${nestingDelimiter}`
|
|
97
|
+
)
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
return flattened;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Flatten and normalizes an array.
|
|
107
|
+
*
|
|
108
|
+
* @param {Record<string, string>} flattened
|
|
109
|
+
* @param {string} key
|
|
110
|
+
* @param {any[]} value
|
|
111
|
+
* @param {string} [nestingDelimiter='__']
|
|
112
|
+
* @param {boolean} [arrayDescomposition=false]
|
|
113
|
+
*/
|
|
114
|
+
function normalizeArray(
|
|
115
|
+
flattened: Record<string, string>,
|
|
116
|
+
key: string,
|
|
117
|
+
value: any[],
|
|
118
|
+
nestingDelimiter = '__',
|
|
119
|
+
arrayDescomposition = false
|
|
120
|
+
): void {
|
|
121
|
+
if (arrayDescomposition) {
|
|
122
|
+
key = `${key}${nestingDelimiter}`;
|
|
123
|
+
|
|
124
|
+
for (let i = 0; i < value.length; i++) {
|
|
125
|
+
if (typeof value[i] === 'object') {
|
|
126
|
+
Object.assign(
|
|
127
|
+
flattened,
|
|
128
|
+
normalize(
|
|
129
|
+
value[i],
|
|
130
|
+
nestingDelimiter,
|
|
131
|
+
arrayDescomposition,
|
|
132
|
+
`${key}${i}${nestingDelimiter}`
|
|
133
|
+
)
|
|
134
|
+
);
|
|
135
|
+
} else {
|
|
136
|
+
flattened[`${key}${i}`] = value[i];
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
} else {
|
|
140
|
+
flattened[key] = value.filter((v) => typeof v !== 'object').join(',');
|
|
141
|
+
}
|
|
142
|
+
}
|